/* Investigate workspace: intentionally scoped so legacy routes retain their layout. */
.investigate-shell {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
}
.investigate-tab-panel > .section:first-child { margin-top: 0; }

.investigate-shell__head {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 1.25rem;
}

.investigate-shell__intro {
  max-width: 48rem;
  margin: 0.45rem 0 0;
  color: var(--ink-dim);
}

/* Dynamic Responses' four filters (ticket 980): one select each, in a row that wraps. */
.dynamic-filters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-4);
  margin: var(--space-3) 0;
}

.dynamic-filter {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font: var(--type-footnote);
  color: var(--ink-dim);
}

/* The four selects share one width (ticket 001080), wide enough for the longest model name, so the row
   reads as one set of controls and not four boxes of four sizes. */
.dynamic-filter select {
  width: 12rem;
  max-width: 100%;
}

/* The reply-time tile draws its p50 and p95 on two lines: a figure is never cut or wrapped part-way. */
.dynamic-tile__line {
  display: block;
}

/* The line a list of identical replies says once, above the table that no longer repeats it. */
.dynamic-alike-note {
  margin: 0 0 var(--space-2);
}

/* The AI agents table's goal cell wraps instead of running past the right edge. */
.ai-agents-table td.ai-agents-goal {
  white-space: normal;
  min-width: 14rem;
  max-width: 26rem;
}

.investigate-workbench {
  display: grid;
  /* The list takes a little over two fifths (ticket 001081): wide enough that a row holding an address, a
     provenance chip and its one-line summary keeps all three on one line at the 1232px content width. */
  grid-template-columns: minmax(20rem, 1fr) minmax(24rem, 1.25fr);
  gap: 1rem;
  align-items: start;
}

.investigate-session-list-panel,
.investigate-session-detail {
  min-width: 0;
}

/* One Contact Report (ticket 001081): the list column is as wide as its row, not a column mostly empty
   under one row, and the selected report takes the rest. 820px and narrower stack as before (below). */
.investigate-workbench.is-single {
  grid-template-columns: fit-content(34rem) minmax(24rem, 1fr);
}

.investigate-session-list-panel {
  margin: 0;
}

.investigate-session-list-panel > .section__head {
  align-items: flex-start;
}

.investigate-session-list-panel .section__hint {
  display: block;
  margin: 0;
}

/* The list scrolls with the page (ticket 933): no inner scroll box, at any width. */
.investigate-session-list {
  display: grid;
  gap: 0.5rem;
}

.investigate-session-row {
  display: grid;
  gap: 0.45rem;
  /* An arrow key's focus move scrolls the row into view clear of the sticky navbar. */
  scroll-margin-top: calc(var(--navbar-h) + 1rem);
  padding: 0.8rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: 0.65rem;
  background: var(--panel);
  cursor: pointer;
}

.investigate-session-row:hover,
.investigate-session-row:focus-visible,
.investigate-session-row.is-selected {
  border-color: var(--border-gold);
  outline: none;
}

.investigate-session-row.is-selected {
  background: rgba(227, 179, 65, 0.08);
  box-shadow: inset 3px 0 var(--gold);
}

.investigate-session-row__head,
.investigate-session-row__meta,
.investigate-detail-facts,
.investigate-detail-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.8rem;
}

/* One row layout (ticket 001081): the address and its provenance chip lead, the one-line summary takes
   the rest of the line on the right. The summary drops under them only when it cannot fit beside them,
   never because a row happens to carry a long chip (the "Private or reserved source" row did). */
.investigate-session-row__head {
  justify-content: flex-start;
}

.investigate-session-row__head > .session-row__ip,
.investigate-session-row__head > .traffic-origin {
  flex: none;
}

.investigate-session-row__head > .session-row__char {
  flex: 1 1 9rem;
  min-width: 0;
  text-align: right;
}

.investigate-session-row__explanation,
.investigate-detail-explanation {
  margin: 0;
  color: var(--ink-dim);
  font-size: var(--fs-xs);
  line-height: 1.45;
}

.investigate-session-row__meta,
.investigate-detail-facts {
  color: var(--ink-muted);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
}

/* Wide screens (ticket 933): the detail stays in view beside the list while the page scrolls. It is
   the one inner scroll, a reading pane that keeps its own position, and it ends above the bottom edge
   by as much as it starts below the navbar. 820px and narrower undo this (end of file). */
.investigate-session-detail {
  display: grid;
  gap: 0.9rem;
  align-content: start;
  min-height: 16rem;
  padding: 1.1rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--panel);
  position: sticky;
  top: calc(var(--navbar-h) + 0.75rem);
  max-height: calc(100dvh - var(--navbar-h) - 1.5rem);
  overflow: auto;
}

/* The next session is loading: the one on screen stays, dimmed, with a spinner in its header. */
.investigate-session-detail.is-loading {
  opacity: 0.6;
  transition: opacity var(--dur-base) ease;
}

.investigate-detail-head {
  position: relative;
}

.investigate-detail-head h3 {
  margin: 0.25rem 0 0.35rem;
  font-family: var(--font-mono);
}

.investigate-detail-spinner {
  position: absolute;
  top: 0.15rem;
  right: 0.15rem;
  width: 1rem;
  height: 1rem;
  border: 2px solid var(--border-strong);
  border-top-color: var(--gold);
  border-radius: 50%;
  animation: investigate-detail-spin 0.8s linear infinite;
}

@keyframes investigate-detail-spin {
  to {
    transform: rotate(360deg);
  }
}

.investigate-session-detail .detail-block {
  margin: 0;
}

/* Phone only: the button that takes the list's place above the opened detail. */
.investigate-workbench > .investigate-detail-back {
  display: none;
}

/* The Live feed is a workspace tab, so its controls stay discoverable: the state button, the count
   and the search box are always present; the request filters live in the page's one Filters dialog
   and exports are a deliberate disclosure. Legacy /live retains its established desktop density
   through console.css. */
.investigate-tab-panel .feed-primary-row {
  flex-wrap: wrap;
}

.investigate-tab-panel .feed-count {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.investigate-tab-panel .feed-primary-row input {
  min-width: min(22rem, 100%);
  flex: 1 1 16rem;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--ink);
  background: var(--panel-deep);
  border: 1px solid var(--border-strong);
  border-radius: 0.6rem;
  padding: 0.42rem 0.65rem;
}

/* The search box's hint reads in the body face at placeholder contrast, not as a dim mono field that
   looks disabled (ticket 001080); what the reader types stays mono. */
.investigate-tab-panel .feed-primary-row input::placeholder {
  font-family: var(--font-ui);
  color: var(--ink-muted);
  opacity: 1;
}

/* A request path wraps in the Live feed table instead of being cut at 34ch: a cut path is evidence the
   reader cannot read. The link's title still holds the whole path. */
.investigate-tab-panel .live-table-scroll td.col-path {
  max-width: none;
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
  overflow-wrap: anywhere;
}

.investigate-tab-panel .feed-export-menu {
  position: relative;
  display: inline-flex;
  flex: none;
}

.investigate-tab-panel .feed-export-menu__panel {
  position: absolute;
  z-index: 4;
  top: calc(100% + 0.35rem);
  right: 0;
  display: grid;
  gap: 0.3rem;
  min-width: 10rem;
  padding: 0.4rem;
  border: 1px solid var(--border-strong);
  border-radius: 0.6rem;
  background: var(--panel-deep);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.34);
}

.investigate-tab-panel .feed-export-menu__panel[hidden] {
  display: none;
}

/* "Show more" and "Load more" sit under the table, one above the other (ticket 979). */
.investigate-tab-panel .feed-pagination {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.7rem;
}

.investigate-tab-panel .feed-pagination .show-more-row {
  margin-top: 0;
}

/* A row's one link is its path (ticket 979): it reads as the row's text, not as a blue link. The
   rows past the drawn range are `hidden`; the card list sets its own `display`, which would beat the
   attribute. */
.feed-exchange-link {
  color: inherit;
  text-decoration: none;
}

.feed-exchange-link:hover {
  text-decoration: underline;
}

.feed-exchange-link:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}

.feed-card[hidden],
.live-table-scroll tr[hidden] {
  display: none;
}

/* The request filters, appended to the Filters dialog while the Live feed is mounted. */
.capture-settings__requests {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
}

.capture-settings__requests .feed-filters-adv input {
  flex: 1 1 12rem;
  min-width: 0;
}

.capture-settings__requests h3 {
  margin: 0 0 0.6rem;
  font-size: var(--fs-md);
}

.investigate-tab-panel .investigate-load-more[hidden] {
  display: none;
}

/* Capture-origin labels describe provenance only. Keep the badge readable alongside an address
   without borrowing the code face used by the address itself. */
.traffic-origin {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  font-family: var(--font-ui);
  font-size: var(--fs-2xs);
  line-height: 1.2;
  white-space: nowrap;
}

.traffic-origin--simulation {
  color: var(--gold-2);
  border-color: var(--border-gold);
  background: var(--gold-soft);
}

.traffic-origin--local {
  color: var(--ink-dim);
  border-color: var(--border-strong);
  background: var(--glass);
}

/* A private/reserved source is infrastructure, not a remote actor: read as a caution, not a
   threat, so it is visually distinct from both the neutral --local pill and any severity color. */
.traffic-origin--private {
  color: var(--ink-body);
  border-color: var(--border-strong);
  background: rgba(139, 148, 158, 0.16);
}

.traffic-origin--mixed {
  color: var(--ink-body);
  border-color: var(--border-strong);
  background: rgba(88, 166, 255, 0.12);
}

.traffic-cell {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 0.45rem;
}

.address-value,
.investigate-code,
.detail-code,
.investigate-session-detail .session-row__ip,
.investigate-session-detail code {
  font-family: var(--font-mono);
}

/* Investigation prose uses the interface face. Captured paths, addresses and bytes opt into the
   code face explicitly above or in their existing classes. */
.investigate-shell .body-note,
.investigate-shell .section__hint,
.investigate-shell__intro,
.investigate-session-row__explanation,
.investigate-detail-explanation,
.investigate-detail-summary,
.investigate-detail-summary__lead,
.investigate-detail-summary__facts,
.investigate-detail-actions,
.agent-session-facts,
.detail-panel .detail-section__summary,
.detail-panel .detail-factual-summary,
.detail-panel .detail-factual-summary__lead,
.detail-panel .detail-factual-summary__facts,
.detail-panel .body-note,
.attacker-view .body-note,
.attacker-view__value,
.attacker-view__meta,
.attacker-view__selection {
  font-family: var(--font-ui);
  line-height: 1.5;
}

.investigate-detail-summary {
  padding: 0.8rem 0.9rem;
  border: 1px solid var(--border-strong);
  border-radius: 0.65rem;
  background: var(--panel-deep);
}

.investigate-detail-summary h4 {
  margin: 0 0 0.45rem;
  color: var(--gold-2);
  font: var(--type-footnote);
  font-weight: 600;
}

.investigate-detail-summary__lead {
  margin: 0;
  color: var(--ink-body);
}

.investigate-detail-summary__facts {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  align-items: baseline;
  gap: 0.4rem 0.65rem;
  margin-top: 0.7rem;
}

.investigate-detail-summary__label,
.detail-factual-summary__label {
  color: var(--ink-muted);
  font-size: var(--fs-2xs);
  font-weight: 600;
}

.investigate-detail-summary__facts code,
.detail-factual-summary__facts code {
  min-width: 0;
  overflow-wrap: anywhere;
  color: var(--ink-body);
}

.investigate-detail-summary__when {
  grid-column: 1 / -1;
  color: var(--ink-dim);
  font-size: var(--fs-2xs);
}

.investigate-detail-badges,
.agent-session-facts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.65rem;
}

.agent-session-facts {
  color: var(--ink-dim);
  font-size: var(--fs-2xs);
}

.investigate-incomplete-note {
  color: var(--ink-dim);
}

/* The exchange drawer is shared by all routes, but these classes belong to its new factual
   Summary / Request / Response organization. Keep technical disclosures available while making
   the first scan readable. */
.detail-factual-summary {
  display: grid;
  gap: 0.55rem;
  padding: 0.8rem 0.9rem;
  border: 1px solid var(--border-strong);
  border-radius: 0.65rem;
  background: var(--panel-deep);
}

.detail-factual-summary__lead {
  margin: 0;
  color: var(--ink-body);
}

.detail-factual-summary__facts {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  align-items: baseline;
  gap: 0.4rem 0.65rem;
}

.detail-factual-summary__facts > * {
  min-width: 0;
  overflow-wrap: anywhere;
}

.detail-factual-summary__response {
  color: var(--ink-body);
  font-weight: 600;
}

.detail-section__summary {
  margin: 0 0 0.55rem;
  color: var(--ink-dim);
}

.detail-subsection__title {
  margin: 0.85rem 0 0.4rem;
  color: var(--ink-dim);
  font: var(--type-footnote);
  font-weight: 600;
}

.detail-block--forensic > summary {
  display: flex;
  align-items: center;
  cursor: pointer;
  list-style: none;
}

.detail-block--forensic > summary::-webkit-details-marker {
  display: none;
}

.detail-block--forensic > summary::before {
  content: "▸";
  display: inline-block;
  min-width: 0.9rem;
  color: var(--ink-muted);
  font-family: var(--font-ui);
  font-size: var(--fs-xs);
  letter-spacing: 0;
}

.detail-block--forensic[open] > summary::before {
  content: "▾";
}

.attacker-view__selection {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.65rem;
  color: var(--ink-dim);
}

.attacker-view__selection-label {
  color: var(--ink-body);
}

.attacker-view__selection-time {
  color: var(--ink-muted);
}

.attacker-view__value,
.attacker-view__meta {
  display: block;
  overflow-wrap: anywhere;
}

.attacker-view__value {
  color: var(--ink-body);
}

.attacker-view__meta {
  color: var(--ink-muted);
  font-size: var(--fs-2xs);
}

.attacker-view__overview-link {
  display: inline-block;
  margin-top: 0.5rem;
}

/* The selected Contact Report's timeline (ticket 001081): the time, method and status read in the body
   face; the captured path keeps the code face, wraps instead of being cut at 34ch, and takes the pane's
   width, so the status sits at the pane's right edge and not at 60 percent of it. */
.investigate-session-detail .timeline {
  font-family: var(--font-ui);
}

.investigate-session-detail .timeline-row {
  flex-wrap: wrap;
  gap: 0.2rem 0.9rem;
}

.investigate-session-detail .timeline-row__time {
  flex: none;
  min-width: 9rem;
  font-variant-numeric: tabular-nums;
}

.investigate-session-detail .timeline-row__method {
  flex: none;
  min-width: 4.5rem;
  color: var(--ink-body);
  font-weight: 600;
}

.investigate-session-detail .timeline-row .col-path {
  flex: 1 1 12rem;
  min-width: 0;
  max-width: none;
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
  overflow-wrap: anywhere;
  font-family: var(--font-mono);
}

.investigate-session-detail .timeline-row > .pill {
  margin-left: auto;
}

/* ---- AI agents rows (ticket 001081) ---------------------------------------------------------------
   A row is two lines (address, score and counts, then what the session did), reads as openable as a
   whole (a bigger chevron, the border and the chevron warm on hover and focus), and "Open address" is a
   quiet text link that takes its button weight back only when the row is under the pointer. */
.agent-group > .agent-group__summary {
  gap: 0.35rem 0.75rem;
  padding: 0.7rem 1rem;
}

/* The disclosure control is a drawn 14px chevron, not a text glyph: the "▸" glyph draws only about half
   its font size across (about 7px at 1.5rem, measured), so a font size cannot make it 12px without a
   far larger box. The open state turns the same shape a quarter turn. */
.agent-group > .agent-group__summary::before,
.agent-group[open] > .agent-group__summary::before {
  content: "";
  flex: none;
  width: 14px;
  height: 14px;
  margin: 0 0.3rem;
  background: currentColor;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
  color: var(--gold-2);
}

.agent-group[open] > .agent-group__summary::before {
  transform: rotate(90deg);
}

.agent-group:has(> .agent-group__summary:hover),
.agent-group:has(> .agent-group__summary:focus-visible) {
  border-color: var(--border-gold);
}

.agent-group > .agent-group__summary .agent-group__facts {
  flex: 1 1 22rem;
}

.agent-group > .agent-group__summary .agent-group__link {
  margin-left: auto;
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  color: var(--ink-dim);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.agent-group > .agent-group__summary:hover .agent-group__link,
.agent-group > .agent-group__summary .agent-group__link:hover,
.agent-group > .agent-group__summary .agent-group__link:focus-visible {
  border-color: var(--border-gold);
  background: var(--glass);
  color: var(--gold-2);
  text-decoration: none;
}

/* ---- Exchange detail panel (ticket 001081) --------------------------------------------------------
   Room under the header rule, and under the path before the Station line; the list behind the panel is
   dimmed to a shape, not a second page of readable text next to the panel's own. */
.detail-overlay {
  background: rgba(0, 0, 0, 0.84);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

/* A sticky box in a padded scroll box sticks at the padding edge, so the head sat 1.4rem below the panel's
   top (an empty bar above it) and drew over the first 1.4rem of what follows it, which left "Summary"
   against the header rule. The negative offset cancels the padding: the head sits where it is laid out. */
.detail-panel > .detail-panel__head {
  top: -1.4rem;
  padding-bottom: 0.9rem;
  margin-bottom: 1.5rem;
}

.detail-panel__head .detail-panel__connector {
  margin-top: 0.5rem;
  font-family: var(--font-ui);
  font-size: var(--fs-2xs);
}

/* A collapsed section says what it holds ("12 headers", "empty", "412 bytes") beside its title, and its
   chevron is a control, not a speck: the same drawn 14px shape as the agent rows' (measured 14px across). */
.detail-block--forensic > summary .detail-block__hint {
  color: var(--ink-dim);
  font: var(--type-footnote);
  font-weight: 400;
}

.detail-panel .detail-block--forensic > summary::before,
.detail-panel .detail-block--forensic[open] > summary::before {
  content: "";
  flex: none;
  width: 14px;
  height: 14px;
  min-width: 0;
  margin-right: 0.4rem;
  background: currentColor;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
  color: var(--gold-2);
}

.detail-panel .detail-block--forensic[open] > summary::before {
  transform: rotate(90deg);
}

@media (max-width: 820px) {
  /* .investigate-shell__head's own display:grid rule moved to decoys-reports.css, scoped to
     .workspace-head.investigate-shell__head (ticket 007 review finding 2) — decoys-reports.css
     loads after this file, so an equal-specificity rule here always lost to its unconditional
     .workspace-head{display:flex}. */

  .investigate-workbench,
  .investigate-workbench.is-single {
    grid-template-columns: 1fr;
  }

  /* The list is the page. A tapped session replaces it with its detail at full width and a Back
     button (ticket 933); with nothing selected there is no detail to show. */
  .investigate-workbench.is-detail-open .investigate-session-list-panel {
    display: none;
  }

  .investigate-workbench:not(.is-detail-open) .investigate-session-detail {
    display: none;
  }

  .investigate-workbench.is-detail-open > .investigate-detail-back {
    display: inline-flex;
    justify-self: start;
    scroll-margin-top: calc(var(--navbar-h) + 1rem);
  }

  .investigate-session-detail {
    position: static;
    max-height: none;
    overflow: visible;
  }

  .investigate-detail-summary__facts,
  .detail-factual-summary__facts {
    grid-template-columns: 1fr;
    gap: 0.25rem;
  }

  .investigate-detail-summary__label,
  .detail-factual-summary__label {
    margin-top: 0.35rem;
  }

  .investigate-detail-summary__when {
    grid-column: auto;
  }
}

@media (max-width: 520px) {
  .investigate-session-detail {
    padding: 0.85rem;
  }
}
