/* boba — a dark, quiet dashboard, written phone-first.

   The rules start at the width of a phone and grow into the desktop layout at
   900px: below it the target list is a drawer over the view and the findings
   pane shows one thing at a time, above it both are columns that sit side by
   side. There is one accent colour and one font; monospace is kept for the two
   places it carries meaning — scanner output, where columns line up, and code
   in a write-up.

   No build step: this file ships inside the binary and has to work on whatever
   browser is to hand at 3am. */

:root {
  color-scheme: dark;

  --bg: #0a0a0a;
  --surface: #101010;
  --surface-2: #161616;
  --surface-3: #1e1e1e;
  --line: #242424;
  --line-2: #323232;

  --text: #ededed;
  --text-2: #9b9b9b;
  --text-3: #6a6a6a;

  /* The accent is the paper colour, not a hue. Nothing on this page needs to be
     told apart by colour that is not already told apart by position, so colour
     is spent only where it carries meaning: a severity, a failure, a worker's
     name in the log. Everything interactive is grey, white or nothing. */
  --accent: #ededed;
  --accent-hi: #ffffff;
  --accent-ink: #0a0a0a;
  --accent-dim: rgba(255, 255, 255, .055);
  --accent-line: rgba(255, 255, 255, .30);

  --amber: #d1a24a;
  --red: #de5d57;
  --red-dim: rgba(222, 93, 87, .1);
  --red-line: rgba(222, 93, 87, .4);
  --codex: #7f9cd0;
  --claude: #a28cc4;

  --ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  --r-sm: 7px;
  --r: 10px;
  --r-lg: 14px;

  --head: 52px;
  --tap: 38px;

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

/* --- base --------------------------------------------------------------- */

* { box-sizing: border-box; }

/* An element with its own display rule ignores the hidden attribute — the
   browser's [hidden] style is weaker than any class selector. Every pane here
   sets display, so without this a hidden tab keeps rendering underneath the one
   that replaced it. */
[hidden] { display: none !important; }

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

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.55 var(--ui);
  -webkit-font-smoothing: antialiased;
}

/* 100dvh, not 100vh: on a phone the browser chrome slides away and 100vh keeps
   counting the space it used to take, which pushes the output pane's last lines
   under the address bar exactly where they are being read. */
body.dash {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

* { scrollbar-width: thin; scrollbar-color: #2c2c2c transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: #2c2c2c; border-radius: 999px;
  border: 3px solid transparent; background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: #3d3d3d; background-clip: content-box; }
::selection { background: rgba(255, 255, 255, .16); }

:focus { outline: none; }
:focus-visible { outline: 2px solid var(--accent-line); outline-offset: 2px; border-radius: 4px; }

svg {
  width: 17px; height: 17px; flex: 0 0 auto;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}

code, .mono { font-family: var(--mono); }

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

button, select, input, textarea { font: inherit; color: inherit; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: var(--tap); padding: 0 13px;
  font-size: 13px; font-weight: 500;
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  white-space: nowrap;
  cursor: pointer;
  transition: background .14s ease, border-color .14s ease, color .14s ease, opacity .14s ease;
}
.btn:hover:not(:disabled) { color: var(--text); background: var(--surface-3); border-color: var(--line-2); }
.btn:disabled { opacity: .35; cursor: not-allowed; }
.btn.primary {
  color: var(--accent-ink); font-weight: 600;
  background: var(--accent); border-color: var(--accent);
}
.btn.primary:hover:not(:disabled) { background: var(--accent-hi); border-color: var(--accent-hi); color: var(--accent-ink); }
.btn.primary:disabled { background: transparent; border-color: var(--line); color: var(--text-3); opacity: 1; }
.btn.danger:hover:not(:disabled) { color: var(--red); background: var(--red-dim); border-color: var(--red-line); }
.btn.solid-danger { color: #fff; background: #ab332d; border-color: #ab332d; font-weight: 600; }
.btn.solid-danger:hover:not(:disabled) { background: #bd3b35; border-color: #bd3b35; color: #fff; }
.btn.block { width: 100%; }
.btn.ghost { background: transparent; border-color: transparent; }
.btn.ghost:hover:not(:disabled) { background: var(--surface-2); }

.icon {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--tap); height: var(--tap); padding: 0 9px;
  color: var(--text-2); background: transparent;
  border: 1px solid transparent; border-radius: var(--r-sm);
  cursor: pointer;
  transition: color .14s ease, background .14s ease;
}
.icon:hover { color: var(--text); background: var(--surface-2); }

input[type="text"], input[type="password"], input[type="search"], input:not([type]), select, textarea {
  width: 100%;
  min-height: var(--tap);
  padding: 8px 11px;
  /* 16px is not a taste: below it, iOS zooms the whole page in on focus and
     leaves the operator panned halfway off a dashboard they were only typing a
     hostname into. The desktop breakpoint takes it back down. */
  font-size: 16px;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  transition: border-color .14s ease, background .14s ease;
}
input::placeholder { color: var(--text-3); }
input:focus, select:focus, textarea:focus {
  border-color: var(--accent-line);
  background: var(--surface);
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .05);
}
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: none; }

select {
  width: auto; max-width: 100%;
  padding-right: 30px;
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5' fill='none' stroke='%239b9b9b' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  text-overflow: ellipsis;
}

input[type="checkbox"] {
  width: 16px; height: 16px; min-height: 0; padding: 0; margin: 0;
  accent-color: var(--accent);
  flex: 0 0 auto;
}

.switch {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: var(--tap); padding: 0 2px;
  font-size: 12.5px; color: var(--text-2);
  white-space: nowrap; cursor: pointer;
}
.switch:hover { color: var(--text); }

/* --- header ------------------------------------------------------------- */

header.top {
  flex: 0 0 auto;
  position: relative;
  z-index: 40;
  display: flex; align-items: center; gap: 10px;
  height: calc(var(--head) + var(--safe-t));
  padding: var(--safe-t) max(10px, var(--safe-r)) 0 max(10px, var(--safe-l));
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.brand { font-size: 15px; font-weight: 650; letter-spacing: -.01em; }

.summary {
  flex: 1 1 auto; min-width: 0;
  color: var(--text-2); font-size: 12.5px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.summary b { color: var(--text); font-weight: 600; }
.summary .path { display: none; }

/* --- layout ------------------------------------------------------------- */

main { flex: 1 1 auto; display: flex; min-height: 0; min-width: 0; }

/* Below the breakpoint the sidebar is a drawer: on a phone the screen belongs
   to whatever is being read, and the target list is a question you ask it. */
aside {
  position: fixed;
  z-index: 35;
  top: calc(var(--head) + var(--safe-t));
  left: 0; bottom: 0;
  width: min(87vw, 340px);
  display: flex; flex-direction: column; gap: 14px;
  padding: 14px max(14px, var(--safe-l)) calc(20px + var(--safe-b));
  background: var(--surface);
  border-right: 1px solid var(--line);
  overflow-y: auto;
  overscroll-behavior: contain;
  transform: translateX(-102%);
  transition: transform .24s cubic-bezier(.4, 0, .2, 1);
}
body.nav-open aside { transform: none; box-shadow: 20px 0 60px rgba(0, 0, 0, .55); }

.scrim {
  position: fixed; inset: 0; z-index: 30;
  background: rgba(5, 5, 7, .62);
  opacity: 0; pointer-events: none;
  transition: opacity .24s ease;
}
body.nav-open .scrim { opacity: 1; pointer-events: auto; }

.view { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; min-height: 0; }

/* --- launcher ----------------------------------------------------------- */

.card {
  flex: 0 0 auto;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 14px;
}

.card h2, .side-head {
  margin: 0 0 12px;
  font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-3);
}

.field { display: block; margin-bottom: 10px; }
.field > span { display: block; margin-bottom: 5px; font-size: 12px; color: var(--text-2); }
/* :not, because a checkbox is an input too — and a checkbox stretched to the
   width of the card is a grey slab with its own sentence pushed off the edge. */
.card input:not([type="checkbox"]), .card select { width: 100%; }
.row { display: flex; gap: 8px; }
.row > * { flex: 1 1 0; min-width: 0; }

.card details { margin: 4px 0 12px; border-top: 1px solid var(--line); padding-top: 10px; }
.card summary {
  display: flex; align-items: center; gap: 6px;
  list-style: none; cursor: pointer;
  font-size: 12px; color: var(--text-3);
}
.card summary::-webkit-details-marker { display: none; }
.card summary:hover { color: var(--text-2); }
.card summary::before {
  content: ""; width: 6px; height: 6px; flex: 0 0 auto;
  border-right: 1.4px solid currentColor; border-bottom: 1.4px solid currentColor;
  transform: rotate(-45deg); transition: transform .16s ease;
  margin-right: 2px;
}
.card details[open] > summary::before { transform: rotate(45deg); }
.card details > *:first-child + * { margin-top: 12px; }

.check {
  display: flex; align-items: flex-start; gap: 9px;
  margin: 0 0 12px; font-size: 12px; line-height: 1.45; cursor: pointer;
}
.check input { margin-top: 2px; }
/* One text run, not three loose nodes: as flex items the words wrapped
   mid-phrase and "start fresh" broke across two lines. */
.check > span { flex: 1 1 auto; color: var(--text-2); }
.check b { color: var(--text); font-weight: 600; }

.hint { margin: 0 0 12px; color: var(--text-3); font-size: 11.5px; line-height: 1.5; }
.hint code { font-size: 11px; color: var(--text-2); }
.hint.known { color: var(--amber); }
.hint.known b { color: var(--text); }

/* --- target list -------------------------------------------------------- */

.side-head { display: flex; align-items: center; justify-content: space-between; margin: 4px 2px -4px; }
.side-head .count { color: var(--text-3); letter-spacing: 0; }

.targets { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.targets .empty { padding: 18px 2px; font-size: 12.5px; color: var(--text-3); }

.target {
  padding: 11px 12px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color .14s ease, background .14s ease;
}
.target:hover { border-color: var(--line-2); }
.target.on { border-color: var(--accent-line); background: var(--accent-dim); }

.target .top { display: flex; align-items: center; gap: 8px; }
.target .name {
  font-weight: 600; font-size: 13.5px; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.target .meta {
  display: flex; flex-wrap: wrap; gap: 4px 12px;
  margin-top: 5px; color: var(--text-3); font-size: 12px;
}
.target .err { margin-top: 6px; color: var(--red); font-size: 12px; word-break: break-word; }

/* Running is the bright one and idle is the dim one, which is the same
   distinction the eye makes across a list without being told a colour. */
.dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: #3d3d3d; }
.dot.run { background: var(--text); box-shadow: 0 0 0 3px rgba(255, 255, 255, .07); }
.dot.wait { background: var(--amber); }
/* A paused hunt is alive and sending nothing. It is the one state that has to be
   told apart from running at a glance, so it gets the ring without the fill —
   the same shape, deliberately hollow. */
.dot.hold { background: transparent; box-shadow: inset 0 0 0 2px var(--text-2); }
.dot.err { background: var(--red); }

.pill {
  flex: 0 0 auto;
  padding: 1px 8px; border-radius: 999px;
  font-size: 10.5px; color: var(--text-3);
  border: 1px solid var(--line-2);
}
.pill.found { color: var(--text); background: rgba(255, 255, 255, .06); border-color: transparent; }

/* --- toolbar ------------------------------------------------------------ */

.toolbar {
  flex: 0 0 auto;
  display: flex; flex-direction: column;
  padding: 8px max(10px, var(--safe-r)) 8px max(10px, var(--safe-l));
  gap: 8px;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.tb-row { display: flex; align-items: center; gap: 10px; min-width: 0; }

.tb-id { display: flex; align-items: baseline; gap: 9px; min-width: 0; flex: 1 1 auto; }
/* The name is what the row is for, so it gets the space and the state gives it
   up: a truncated "zebra…" beside a complete "running · pid 78192" is the wrong
   half of the line to lose. */
.tb-id h1 {
  flex: 1 1 auto; min-width: 0;
  margin: 0; font-size: 15px; font-weight: 600; letter-spacing: -.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tb-id .state {
  flex: 0 1 auto; min-width: 0;
  color: var(--text-3); font-size: 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tb-id .state .pid { display: none; }
.tb-id .state.run { color: var(--text); }
.tb-id .state.hold { color: var(--amber); }

.tb-actions { display: flex; align-items: center; gap: 5px; flex: 0 0 auto; }
.tb-actions .btn { min-height: 36px; padding: 0 8px; }
.tb-actions .lbl { display: none; }

/* The run window belongs to the button it qualifies, so the two are one
   control: "resume" on its own is the question this pairing answers — for how
   long — and an operator picking it out of a separate options panel is an
   operator who will not pick it. */
.split { display: inline-flex; align-items: stretch; }
.split .btn { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.split select {
  width: auto; min-height: 36px;
  padding: 0 19px 0 8px;
  font-size: 12px; color: var(--text-2);
  background-color: var(--surface-2);
  /* The chevron is sized down with the control. At the sheet's default it is
     nearly as wide as the "8h" beside it and the pair reads as an icon with a
     label rather than a value with a picker. */
  background-position: right 4px center;
  background-size: 11px 11px;
  border-left: none;
  border-top-left-radius: 0; border-bottom-left-radius: 0;
}
.split select:disabled { opacity: .35; }

/* The second row scrolls sideways rather than wrapping: a phone gets the tabs
   under a thumb and the options one flick away, instead of a toolbar that grows
   a third row and eats the output. */
.tb-row.sub {
  gap: 10px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.tb-row.sub::-webkit-scrollbar { display: none; }

.tabs {
  display: flex; gap: 2px; flex: 0 0 auto;
  padding: 3px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 999px;
}
.tab {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 14px;
  background: transparent; border: 0; border-radius: 999px;
  color: var(--text-3); font-size: 12.5px; font-weight: 500;
  white-space: nowrap; cursor: pointer;
  transition: color .14s ease, background .14s ease;
}
.tab:hover { color: var(--text-2); }
.tab.on { color: var(--text); background: var(--surface-3); }

.badge {
  min-width: 17px; padding: 1px 6px;
  border-radius: 999px; background: var(--surface-3);
  color: var(--text-3); font-size: 10.5px; text-align: center;
}
/* :not(.hot), because ".tab.on .badge" outranks ".badge.hot" on specificity —
   without it the count on the open tab is dark text on a dark chip. */
.tab.on .badge:not(.hot) { background: var(--bg); }
.badge.hot { background: var(--accent); color: var(--accent-ink); font-weight: 600; }

.tb-opts { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; margin-left: auto; }
.tb-opts .opt { display: flex; align-items: center; gap: 10px; }
.tb-opts select { max-width: 44vw; font-size: 12.5px; min-height: 32px; padding: 4px 28px 4px 9px; }

/* --- output ------------------------------------------------------------- */

.out {
  flex: 1 1 auto;
  margin: 0;
  padding: 12px max(12px, var(--safe-r)) calc(16px + var(--safe-b)) max(12px, var(--safe-l));
  overflow: auto;
  overscroll-behavior: contain;
  white-space: pre-wrap;
  word-break: break-word;
  background: var(--bg);
  font: 12px/1.6 var(--mono);
  color: #c2c6cd;
}
.out .l { display: block; }
.out .codex { color: var(--codex); }
.out .claude { color: var(--claude); }
.out .note { color: var(--text-2); }
.out .head { color: var(--text-3); }
.out .bad { color: var(--red); }
.out .good { color: var(--text); }

.empty { padding: 26px 4px; color: var(--text-3); font-size: 13px; line-height: 1.6; }

/* --- steering ----------------------------------------------------------- */

/* Talking to a running hunt. It sits at the bottom of the output pane, where a
   message box belongs on a phone: above the thumb, under the thing it is about,
   and out of the way of the log it is answering. */
.steer {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 6px;
  padding: 8px max(10px, var(--safe-l)) 8px max(10px, var(--safe-r));
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.steer select { flex: 0 0 auto; max-width: 36vw; font-size: 12.5px; min-height: var(--tap); padding: 4px 26px 4px 9px; }
.steer input { flex: 1 1 auto; min-width: 0; min-height: var(--tap); }
.steer .btn { flex: 0 0 auto; min-height: var(--tap); padding: 0 10px; }
.steer .lbl { display: none; }

.steer-note {
  flex: 0 0 auto;
  margin: 0;
  padding: 0 max(10px, var(--safe-l)) calc(8px + var(--safe-b));
  background: var(--surface);
  color: var(--text-3); font-size: 11.5px; line-height: 1.5;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.steer-note b { color: var(--text-2); font-weight: 600; font-family: var(--mono); font-size: 11px; }
.steer-note .waiting { color: var(--amber); }
.steer-note .taken { color: var(--text-3); }

/* --- findings ----------------------------------------------------------- */

/* One pane at a time on a phone: the ledger, then the write-up with a way back.
   A split view at 390px gives neither half enough room to be read. */
.ledger { flex: 1 1 auto; display: flex; min-height: 0; min-width: 0; }
.ledger-list {
  flex: 1 1 auto; min-width: 0;
  overflow-y: auto; overscroll-behavior: contain;
  background: var(--bg);
  padding-bottom: var(--safe-b);
}
.ledger-pane { display: none; flex: 1 1 auto; min-width: 0; min-height: 0; flex-direction: column; }
.ledger.reading .ledger-list { display: none; }
.ledger.reading .ledger-pane { display: flex; }

.doc-bar {
  flex: 0 0 auto;
  display: flex; align-items: center;
  padding: 6px max(8px, var(--safe-l));
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}

.finding {
  display: flex; flex-direction: column; gap: 6px;
  padding: 13px max(12px, var(--safe-l));
  border-bottom: 1px solid var(--line);
  cursor: pointer;
  transition: background .12s ease;
}
.finding:hover { background: var(--surface-2); }
.finding.on { background: var(--accent-dim); box-shadow: inset 2px 0 0 var(--accent); }
.finding .head { display: flex; align-items: center; gap: 8px; }
.finding .title { line-height: 1.45; font-size: 13.5px; }
.finding .id { color: var(--text-3); font-size: 12px; font-family: var(--mono); }
.finding .sub { display: flex; flex-wrap: wrap; gap: 4px 12px; color: var(--text-3); font-size: 12px; }

.sev {
  flex: 0 0 auto;
  padding: 2px 7px; border-radius: 4px;
  border: 1px solid var(--line-2);
  font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-3);
}
.sev.critical { color: #fff; background: #8c2f29; border-color: #9e3a33; }
.sev.high { color: var(--red); border-color: var(--red-line); }
.sev.medium { color: var(--amber); border-color: rgba(209, 162, 74, .34); }

.ledger-doc {
  /* min-height:0 is what makes the overflow scroll rather than clip. Without it
     a flex or grid item keeps its content's intrinsic height, the pane grows
     past the viewport, and the tail of a long write-up is unreachable — which
     is exactly what a submission is: long. */
  flex: 1 1 auto; min-width: 0; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
  padding: 18px max(14px, var(--safe-r)) calc(40px + var(--safe-b)) max(14px, var(--safe-l));
}
.ledger-doc h1 { margin: 0 0 8px; font-size: 18px; font-weight: 650; line-height: 1.35; letter-spacing: -.01em; }
.ledger-doc h2 { margin: 24px 0 8px; font-size: 14px; font-weight: 600; }
.ledger-doc h3 { margin: 18px 0 6px; font-size: 12.5px; font-weight: 600; color: var(--text-2); }
.ledger-doc p, .ledger-doc li { margin: 0 0 9px; }
.ledger-doc ul, .ledger-doc ol { margin: 0 0 12px; padding-left: 20px; }
.ledger-doc strong { color: #fff; font-weight: 600; }
.ledger-doc code {
  font-size: 12px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 4px; padding: 1px 5px;
  word-break: break-word;
}
.ledger-doc pre {
  margin: 0 0 14px; padding: 12px;
  font: 12px/1.6 var(--mono);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  overflow-x: auto; overscroll-behavior-x: contain;
}
.ledger-doc pre code { background: none; border: 0; padding: 0; font-size: inherit; word-break: normal; }
.ledger-doc .meta {
  margin-bottom: 18px; padding-bottom: 14px;
  border-bottom: 1px solid var(--line);
  color: var(--text-3); font-size: 12px;
}
.ledger-doc .meta .path { font-family: var(--mono); font-size: 11px; word-break: break-all; }

/* --- scope -------------------------------------------------------------- */

.scope {
  flex: 1 1 auto;
  overflow-y: auto; overscroll-behavior: contain;
  padding: 18px max(14px, var(--safe-r)) calc(40px + var(--safe-b)) max(14px, var(--safe-l));
  max-width: 860px;
}
.scope .mode { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin-bottom: 6px; }
.scope h2 { margin: 0; font-size: 16px; font-weight: 650; letter-spacing: -.01em; }
.scope .why { margin: 0 0 20px; color: var(--text-2); line-height: 1.6; }
.scope .why strong { color: var(--text); }
.scope h3 {
  margin: 22px 0 8px;
  font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-3);
}
.scope ul { list-style: none; margin: 0; padding: 0; }
.scope li {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  margin-bottom: 5px; padding: 9px 11px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm);
}
.scope li .asset { font-family: var(--mono); font-size: 12.5px; word-break: break-all; }
.scope li .covers { margin-left: auto; color: var(--text-3); font-size: 12px; text-align: right; }
.scope li.out .asset { color: var(--red); }

.mode-tag {
  padding: 2px 9px; border-radius: 999px;
  border: 1px solid var(--line-2); color: var(--text-3); font-size: 11px;
}
.mode-tag.exact { color: var(--text-2); }
.mode-tag.estate { color: var(--text); border-color: var(--accent-line); }

/* --- coverage ----------------------------------------------------------- */

/* Coverage is a grid of counts and a list of what is missing. The counts are
   large because they are read from across the room at 3am; the gaps are the
   half that is actually actionable, so they get their own heading rather than a
   footnote. */
.cover {
  flex: 1 1 auto;
  overflow-y: auto; overscroll-behavior: contain;
  padding: 18px max(14px, var(--safe-r)) calc(40px + var(--safe-b)) max(14px, var(--safe-l));
  max-width: 940px;
}
.cover-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin-bottom: 6px; }
.cover-head h2 { margin: 0; font-size: 16px; font-weight: 650; letter-spacing: -.01em; }
.cover .why { margin: 0 0 18px; color: var(--text-2); line-height: 1.6; }
.cover .why code {
  font-family: var(--mono); font-size: 11.5px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 4px; padding: 1px 5px;
}
.cover h3 {
  margin: 24px 0 8px;
  font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-3);
}

.tiles {
  display: grid; gap: 8px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.tile {
  display: flex; flex-direction: column; gap: 3px;
  padding: 12px 13px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm);
}
.tile .v { font-size: 21px; font-weight: 600; letter-spacing: -.02em; line-height: 1.15; }
.tile .v small { font-size: 13px; font-weight: 400; color: var(--text-3); letter-spacing: 0; }
.tile .k { font-size: 12px; color: var(--text-2); }
.tile .n { font-size: 11px; color: var(--text-3); line-height: 1.45; }

.gaps { list-style: none; margin: 0; padding: 0; }
.gaps li {
  padding: 10px 12px 10px 26px;
  margin-bottom: 5px;
  position: relative;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm);
  color: var(--text-2); line-height: 1.5;
}
.gaps li::before {
  content: ""; position: absolute; left: 12px; top: 17px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--amber);
}
.cover .foot { margin: 22px 0 0; color: var(--text-3); font-size: 11.5px; line-height: 1.6; }

/* --- modal -------------------------------------------------------------- */

/* Deleting an engagement is the one action here with no undo, so it gets a
   dialog with the name typed back rather than a confirm() a thumb can dismiss
   by accident. */
.modal {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: flex-end; justify-content: center;
  padding: 0;
  background: rgba(5, 5, 7, .66);
}
.modal-box {
  width: 100%; max-width: 460px;
  padding: 20px max(18px, var(--safe-l)) calc(20px + var(--safe-b)) max(18px, var(--safe-r));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: 0 -20px 60px rgba(0, 0, 0, .6);
  animation: sheet .22s cubic-bezier(.4, 0, .2, 1) both;
}
@keyframes sheet { from { transform: translateY(14px); opacity: 0; } }
.modal-box h2 { margin: 0 0 10px; font-size: 16px; font-weight: 650; letter-spacing: -.01em; }
.modal-box p { margin: 0 0 12px; color: var(--text-2); font-size: 13px; line-height: 1.55; }
.modal-box p .path { font-family: var(--mono); font-size: 11.5px; color: var(--text-3); word-break: break-all; }
.modal-box b { color: var(--text); font-weight: 600; }
.modal-box .warn { color: var(--amber); }
.modal-actions { display: flex; gap: 8px; margin-top: 16px; }
.modal-actions .btn { flex: 1 1 0; min-height: 42px; }

/* A proof image, under the write-up it belongs to. Tapping opens it full size:
   on a phone the inline copy is a thumbnail of a 1440px capture, and the detail
   that makes it evidence — the account menu, the response body — is only
   legible at full size. */
.ledger-doc .shot { margin: 18px 0 0; }
.ledger-doc .shot img {
  display: block; width: 100%; height: auto;
  background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-sm);
}
.ledger-doc .shot figcaption {
  margin-top: 6px;
  font-family: var(--mono); font-size: 11.5px; color: var(--text-3);
}

/* --- toast -------------------------------------------------------------- */

#toast {
  position: fixed; z-index: 60;
  left: max(12px, var(--safe-l)); right: max(12px, var(--safe-r));
  bottom: calc(12px + var(--safe-b));
  display: flex; flex-direction: column-reverse; gap: 8px;
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  padding: 11px 14px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-left: 2px solid var(--text-3);
  border-radius: var(--r-sm);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .5);
  font-size: 13px;
  animation: rise .2s ease both;
}
@keyframes rise { from { opacity: 0; transform: translateY(8px); } }
.toast.bad { border-left-color: var(--red); }
.toast.good { border-left-color: var(--text); }

/* --- login -------------------------------------------------------------- */

body.login {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; min-height: 100dvh;
  padding: 24px max(20px, var(--safe-r)) calc(24px + var(--safe-b)) max(20px, var(--safe-l));
}
body.login::before {
  content: ""; position: fixed; inset: 0; pointer-events: none;
  background: radial-gradient(60vw 40vh at 50% 0%, rgba(255, 255, 255, .035), transparent 70%);
}
.gate {
  position: relative;
  width: min(360px, 100%);
  padding: 26px 22px 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .5);
}
.gate .brand { display: block; font-size: 17px; margin-bottom: 16px; }
.gate input { margin-bottom: 10px; }
.gate .btn { min-height: 42px; }
.gate .err { min-height: 1.3em; margin-top: 12px; color: var(--red); font-size: 12.5px; }

/* --- wide --------------------------------------------------------------- */

@media (min-width: 900px) {
  :root { --tap: 34px; }

  body { font-size: 14px; }

  input[type="text"], input[type="password"], input[type="search"], input:not([type]), select, textarea {
    font-size: 13px;
  }

  .nav-toggle { display: none; }

  aside {
    position: static;
    width: 336px; flex: 0 0 336px;
    top: auto;
    padding: 14px;
    transform: none;
    transition: none;
    box-shadow: none;
  }
  .scrim { display: none; }

  .summary .path { display: inline; }

  .toolbar { padding: 9px 14px; gap: 9px; }
  .tb-actions .lbl { display: inline; }
  .tb-actions .btn { min-height: var(--tap); padding: 0 12px; }
  .split select { min-height: var(--tap); font-size: 12.5px; padding: 0 21px 0 9px; }
  .tb-id .state .pid { display: inline; }
  .tb-opts select { max-width: 260px; }

  .out { padding: 12px 16px; font-size: 12.5px; }

  /* Wide enough for both halves: the ledger stays on screen while a write-up is
     read beside it, which is how one gets compared to the next. */
  .ledger {
    display: grid;
    grid-template-columns: minmax(280px, 34%) 1fr;
    grid-template-rows: minmax(0, 1fr);
  }
  .ledger-list { border-right: 1px solid var(--line); background: var(--surface); min-height: 0; }
  .ledger-pane { display: flex; min-height: 0; }
  .ledger.reading .ledger-list { display: block; }
  .doc-bar { display: none; }
  .finding { padding: 12px 14px; }
  .ledger-doc { padding: 24px 28px 60px; max-width: 860px; }

  .scope { padding: 24px 28px 60px; }
  .cover { padding: 24px 28px 60px; }

  .steer { padding: 9px 14px; gap: 8px; }
  .steer .lbl { display: inline; }
  .steer .btn { padding: 0 12px; }
  .steer select { max-width: 200px; }
  .steer-note { padding: 0 14px 9px; }

  .modal { align-items: center; padding: 24px; }
  .modal-box { border-radius: var(--r-lg); padding: 22px; }

  #toast { left: auto; right: 16px; bottom: 16px; max-width: 420px; }
}

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