/* ===================================================================
   Adment-aligned visual layer.
   Loaded AFTER style.css and deliberately overrides it. The reference
   captures in adment-implementation-reference-20260911/live-20260911
   are 1349x722 CSS px; every fixed number below was measured off those
   pixels (rail edge x=72, panel 113..1308, row pitch 48px, case layer
   left edge x=307, ad layer x=377) rather than estimated by eye.

   What this replaces, per the user's correction:
     - 240px dark teal sidebar  -> 72px white icon rail
     - teal accent              -> blue accent on light grey
     - big stat cards           -> compact underline tabs with counts
     - centred report modal     -> right-hand overlay layer
   =================================================================== */

:root {
  --bg: #f5f6f8;
  --panel: #ffffff;
  --line: #e6e8eb;
  --line-soft: #eef0f2;
  --text: #101828;
  --text-2: #344054;
  --muted: #667085;
  --accent: #2f6fed;
  --accent-soft: #e8f0fe;
  --accent-line: #2f6fed;
  --chip: #f2f4f7;
  --chip-line: #e4e7ec;
  --r-sm: 5px;
  --r-md: 7px;
  --r-lg: 9px;
  --rail: 72px;
}

html, body { background: var(--bg); color: var(--text); }
body { font-size: 14px; }

/* style.css (legacy) styles every bare <button> teal. That is the single
   source of the colour contradiction: a page built from plain buttons came
   out teal while the rest of the shell is blue. The primary action is the
   accent, everywhere. Components that need another look (.linkbtn, .iconbtn,
   .morechip, ...) set their own background after this rule. */
button, .button {
  background: var(--accent);
  color: #fff;
  border-radius: var(--r-md);
  padding: 9px 14px;
  font-size: 13.5px;
}
button:hover, .button:hover { filter: brightness(.94); }
button:disabled { opacity: .45; cursor: default; filter: none; }

/* ---- Icon rail ---------------------------------------------------- */
aside {
  position: fixed;
  inset: 0 auto 0 0;
  width: var(--rail);
  background: #fff;
  border-right: 1px solid var(--line);
  color: var(--muted);
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  z-index: 30;
}
.rail-logo {
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #1f2a37;
}
.rail-logo svg { width: 26px; height: 26px; }
aside nav { display: flex; flex-direction: column; gap: 2px; padding: 4px 6px; }
aside nav button {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: transparent;
  border: 0;
  border-radius: var(--r-md);
  padding: 9px 2px 8px;
  color: var(--muted);
  font-size: 10.5px;
  line-height: 1.25;
  text-align: center;
  cursor: pointer;
  white-space: normal;
}
aside nav button svg { width: 21px; height: 21px; flex: none; }
aside nav button span { display: block; }
aside nav button:hover { background: #f4f6f9; color: var(--text-2); }
aside nav button.active { background: var(--accent-soft); color: var(--accent); }
aside nav button.active svg { color: var(--accent); }

.rail-bottom { margin-top: auto; padding-bottom: 12px; }
.rail-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  color: var(--muted);
  font-size: 10.5px;
  padding: 10px 2px 6px;
}
.rail-brand svg { width: 20px; height: 20px; }
.rail-user {
  display: block;
  margin: 6px auto 0;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  border: 0;
  padding: 0;
  display: grid;
  place-items: center;
  cursor: pointer;
}
.rail-user svg { width: 19px; height: 19px; }
aside .buildstamp {
  display: block;
  font-size: 8.5px;
  text-align: center;
  color: #98a2b3;
  padding: 0 4px;
  word-break: break-all;
  line-height: 1.3;
}

/* ---- Page frame --------------------------------------------------- */
main {
  margin-left: var(--rail);
  padding: 18px 40px 34px;
  max-width: none;
}
header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 0 0 14px;
}
header h1 {
  font-size: 27px;
  font-weight: 650;
  letter-spacing: -.4px;
  margin: 0;
  color: var(--text);
}
.headeractions { display: flex; align-items: center; gap: 10px; }
.ghostbtn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: #fff;
  color: var(--text-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 9px 15px;
  font-size: 13.5px;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
}
.ghostbtn:hover { border-color: #cfd4dc; filter: none; }
.ghostbtn svg { width: 15px; height: 15px; }

/* The environment caveat is a single quiet strip, not a yellow block
   that owns the first screen. */
.notice, #hijdemo, #biddemo, #setdemo {
  background: #fff;
  border: 1px solid var(--line);
  border-left: 3px solid #f0b429;
  border-radius: var(--r-md);
  padding: 8px 13px;
  margin-bottom: 12px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-2);
}

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 0;
  margin-bottom: 16px;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
}
.panel > h2 { padding: 16px 20px 0; }

/* ---- List panel: tabs -> filters -> table -> pager ----------------- */
.casepanel { display: flex; flex-direction: column; }
.tabrow {
  display: flex;
  align-items: stretch;
  gap: 4px;
  border-bottom: 1px solid var(--line);
  padding: 0 14px;
  overflow-x: auto;
}
.tabrow button {
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  color: var(--muted);
  font-size: 13.5px;
  padding: 13px 12px 11px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  white-space: nowrap;
}
.tabrow button:hover { color: var(--text-2); filter: none; }
.tabrow button[aria-pressed="true"] { color: var(--accent); border-bottom-color: var(--accent-line); font-weight: 600; }
.tabrow .tabcount {
  background: var(--chip);
  color: var(--muted);
  border-radius: 999px;
  font-size: 11.5px;
  padding: 1px 7px;
  font-variant-numeric: tabular-nums;
}
.tabrow button[aria-pressed="true"] .tabcount { background: var(--accent-soft); color: var(--accent); }

/* `.filterbar` is now only a vertical stack: the primary row, then the
   optional secondary panel. It holds no horizontal layout of its own.

   `align-items` is pinned here because the legacy sheet still carries
   `#hijfilters { align-items: end }` - an ID, so it outranks this class. On
   the old row that was inert; on a column it stops the rows stretching, and
   the primary row then sized to its content (1361px inside a 1195px panel)
   and hung off the left edge, cutting the Date pill in half. */
.filterbar {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  padding: 12px 16px;
}
/* The primary row must stay one line: wrapping search onto a second row is
   what pushed the table down far enough to clip its last row. `nowrap` plus
   `min-width: 0` means the controls absorb pressure by shrinking.

   `.filtermore` used to sit *inside* this row, so opening it competed for
   width and shrank every pill below its own label ("Country" -> "C...",
   "Advertiser" -> "Advert...") exactly when the user had opened the panel to
   filter hardest. It is now a sibling in the column above, so the row keeps
   the full width whether the panel is open or closed. */
.filterrow {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: nowrap;
  min-width: 0;
}
/* Pills shrink to share one line, but never below the width of their own
   label: the row is only useful if a reader can still tell the controls
   apart. 104px clears the longest label ("Advertiser") plus the padding and
   the chevron. Seven pills at the floor still leave the search box its
   minimum, so nothing is pushed onto a second line.

   Without the floor, adding the Domain filter silently truncated "Country"
   to "C..." - the same class of defect the row was restructured to fix. */
.filterrow > .fpill { flex: 0 1 auto; min-width: 104px; }
/* "More filters" is a fixed label with no value to abbreviate: shrinking it
   produced "More filt...", which reads as a defect rather than a control. */
.filterrow > .morechip { flex: 0 0 auto; }
.filterbar select,
.filterbar input[type="date"] {
  appearance: none;
  background: #fff url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23667085' stroke-width='2'><path d='M6 9l6 6 6-6'/></svg>") no-repeat right 8px center;
  background-size: 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 8px 28px 8px 11px;
  font-size: 13px;
  color: var(--text-2);
  min-height: 36px;
}
.filterbar input[type="date"] { background-image: none; padding-right: 10px; }
.filterbar select:hover { border-color: #cfd4dc; }
/* A filter pill reads as its own name until a value is chosen, then shows
   "Name: value" and takes the accent, so an active filter is visible without
   opening the control. */
.fpill { max-width: 220px; text-overflow: ellipsis; }
.fpill.on {
  border-color: var(--accent);
  color: var(--accent);
  background-color: var(--accent-soft);
}
.filterbar .iconbtn { margin-left: 4px; }

.filterbar .fsearch { position: relative; margin-left: auto; flex: 0 1 auto; min-width: 0; }
.filterbar .fsearch input {
  width: 210px;
  min-width: 120px;
  max-width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 8px 11px 8px 32px;
  font-size: 13px;
  min-height: 36px;
}
.filterbar .fsearch svg {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  width: 14px;
  height: 14px;
  color: var(--muted);
  pointer-events: none;
}
.filterbar .checkline { font-size: 12.5px; color: var(--muted); gap: 5px; }
.filterbar .linkbtn { font-size: 12.5px; font-family: inherit; }
/* Secondary filters. Real capability that does not fit the reference's one
   line: parked behind a toggle instead of being deleted. The dot marks that
   something inside is active, so a hidden filter can never silently narrow
   the list without a visible trace. */
.filtermore {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  /* Parent is a column with its own 8px gap; no extra top margin. */
  padding: 12px 0 2px;
  border-top: 1px solid var(--line-soft);
}
.filtermore[hidden] { display: none; }
.filtermore label { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--muted); }
.filtermore .muted.small { flex-basis: 100%; margin: 0; font-size: 11.5px; line-height: 1.5; }
.morechip.dot::after {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: 6px;
  border-radius: 50%;
  background: var(--accent);
  vertical-align: middle;
}
/* In the filter bar the toggle sits in a row of 36px controls, so it matches
   them there. The compact `.morechip` base is still what the case layer uses. */
.filterbar .morechip {
  min-height: 36px;
  padding: 8px 12px;
  font-size: 13px;
  border-radius: var(--r-md);
  background: #fff;
  border-color: var(--line);
}
.filterbar .morechip.on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.filtercount {
  padding: 0 16px 10px;
  margin: -2px 0 0;
  font-size: 12px;
  color: var(--muted);
}

/* Table: 48px pitch, column separators, no inner card. */
.casetable { font-size: 13px; width: 100%; border-collapse: collapse; }
.casetable thead th {
  background: #fff;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  border-right: 1px solid var(--line-soft);
  padding: 0;
  color: var(--muted);
  font-weight: 500;
  font-size: 12.5px;
  white-space: nowrap;
}
.casetable thead th:last-child { border-right: 0; }
.casetable .sortbtn {
  width: 100%;
  text-align: left;
  font-weight: 500;
  font-size: 12.5px;
  color: var(--muted);
  padding: 11px 12px;
  display: flex;
  align-items: center;
  gap: 5px;
}
.casetable .sortbtn:hover { color: var(--text-2); }
.casetable .sortbtn.on { color: var(--text-2); font-weight: 600; }
/* Every sortable column shows the affordance; the active one resolves to a
   single arrow. Hiding it on inactive columns hid that they sort at all. */
.casetable .sortmark {
  width: 13px;
  height: 13px;
  margin-left: 4px;
  color: #c3c9d4;
  flex: none;
}
.casetable .sortmark.on { color: var(--accent); }
.casetable .sortbtn { display: inline-flex; align-items: center; }
/* Long values truncate on one line and keep the full text in the tooltip,
   which is what holds the 48px row pitch. */
.ellip {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

.casetable tbody td {
  border-top: 0;
  border-bottom: 1px solid var(--line-soft);
  border-right: 1px solid var(--line-soft);
  padding: 0 12px;
  height: 48px;
  vertical-align: middle;
  color: var(--text-2);
  max-width: 190px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.casetable tbody td:last-child { border-right: 0; }
.casetable tbody tr:hover { background: #fafbfc; }
.casetable tbody tr:focus { outline: 2px solid var(--accent); outline-offset: -2px; }
.casetable .dimrow { background: #fcfcfd; color: #98a2b3; }
.casetable .numcol { text-align: left; }

/* Identifier chip, status chip, tag chip - the three chip shapes the
   reference list uses, and nothing else. */
.idchip {
  display: inline-block;
  background: var(--chip);
  border: 1px solid var(--chip-line);
  border-radius: var(--r-sm);
  padding: 3px 8px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  color: var(--text-2);
  white-space: nowrap;
}
.statuschip {
  display: inline-block;
  border-radius: var(--r-sm);
  padding: 3px 9px;
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
  background: var(--accent-soft);
  color: #1d4ed8;
}
.statuschip.s-investigating { background: #fff4e5; color: #a95c00; }
.statuschip.s-ready_for_action { background: #ecf3ff; color: #1d4ed8; }
.statuschip.s-resolved { background: #e7f6ec; color: #1c6b38; }
.statuschip.s-monitoring { background: #f2f4f7; color: #475467; }
.tagchip {
  display: inline-block;
  background: var(--accent-soft);
  border: 1px solid #d6e4fd;
  border-radius: var(--r-sm);
  padding: 3px 8px;
  font-size: 12px;
  color: #1d4ed8;
  white-space: nowrap;
}
.cellmuted { color: #98a2b3; }
.srcicon { width: 17px; height: 17px; vertical-align: -3px; }
.notedot { color: #98a2b3; display: inline-block; }

.pagerbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 16px;
  flex-wrap: wrap;
}
.pagerbar .perpage { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted); }
.pagerbar .perpage select {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 5px 9px;
  font-size: 12.5px;
}
.pagenums { display: flex; align-items: center; gap: 5px; }
.pagenums button {
  min-width: 30px;
  height: 30px;
  padding: 0 7px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--text-2);
  font-size: 12.5px;
  display: grid;
  place-items: center;
}
.pagenums button:hover:not(:disabled) { background: #f4f6f9; filter: none; }
.pagenums button[aria-current="true"] { border-color: var(--accent); color: var(--accent); font-weight: 600; }
.pagenums button:disabled { color: #cfd4dc; cursor: default; }
.pagenums svg { width: 15px; height: 15px; }
.pagegap { color: var(--muted); font-size: 12.5px; padding: 0 2px; }

/* ---- Case layer: right overlay, not a centred modal ---------------- */
#casedetail, #addetail {
  position: fixed;
  margin: 0;
  border: 0;
  border-radius: 0;
  padding: 0;
  top: 0;
  bottom: 0;
  right: 0;
  left: auto;
  height: 100%;
  max-height: none;
  max-width: none;
  background: #fff;
  box-shadow: -14px 0 40px rgba(16, 24, 40, .16);
}
/* Measured: case layer starts at x=307 of 1349 (77.2% wide); the ad
   layer sits on top of it, inset a further 70px (x=377). */
#casedetail { width: 77.2vw; }
#addetail { width: 72vw; }
#casedetail[open], #addetail[open] { display: flex; flex-direction: column; }
#casedetail::backdrop, #addetail::backdrop { background: rgba(16, 24, 40, .45); }

.layerhead {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  /* Reference layer header measures 101px tall; the padding is what sets it. */
  padding: 33px 28px;
  border-bottom: 1px solid var(--line);
}
.layerhead h2 { font-size: 21px; font-weight: 650; margin: 0; color: var(--text); }
.layerhead .lh-left { display: flex; align-items: center; gap: 12px; min-width: 0; }
.iconbtn {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: #fff;
  color: var(--text-2);
  display: grid;
  place-items: center;
  padding: 0;
  flex: none;
}
.iconbtn:hover { background: #f4f6f9; filter: none; }
.iconbtn svg { width: 16px; height: 16px; }
#casebody, #adbody { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 8px 28px 28px; }

/* Label-value rows with thin separators - the reference case layout. */
.fieldrow {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  padding: 14px 0;
  border-bottom: 1px solid var(--line-soft);
  min-height: 56px;
}
.fieldrow > .flabel {
  flex: 0 0 200px;
  color: var(--muted);
  font-size: 13.5px;
  padding-top: 4px;
}
.fieldrow > .fvalue { flex: 1 1 auto; min-width: 0; font-size: 13.5px; color: var(--text-2); }
.fvalue .chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.addlink {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: none;
  border: 0;
  color: var(--accent);
  font-size: 13.5px;
  padding: 2px 0;
  cursor: pointer;
}
.addlink svg { width: 16px; height: 16px; }
.addlink:hover { filter: none; text-decoration: underline; }
.inlineinput {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 7px 10px;
  font-size: 13.5px;
  min-width: 220px;
}
.inlineselect {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 6px 9px;
  font-size: 13px;
}
.morechip {
  background: var(--chip);
  border: 1px solid var(--chip-line);
  border-radius: var(--r-sm);
  padding: 3px 8px;
  font-size: 12px;
  color: var(--text-2);
  cursor: pointer;
}
.switch {
  width: 40px;
  height: 22px;
  border-radius: 999px;
  background: #d0d5dd;
  border: 0;
  padding: 0;
  position: relative;
  cursor: pointer;
  transition: background .15s;
}
.switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  transition: transform .15s;
  box-shadow: 0 1px 3px rgba(16, 24, 40, .2);
}
.switch[aria-checked="true"] { background: var(--accent); }
.switch[aria-checked="true"]::after { transform: translateX(18px); }
.savehint { font-size: 12px; color: var(--muted); margin-left: 10px; }
.savehint.err { color: #b42318; }

.sectionhead {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 22px 0 10px;
}
.sectionhead h3 { font-size: 15px; font-weight: 600; margin: 0; color: var(--text); }
.sectionhead small { color: var(--muted); font-size: 12px; }

/* Qualifications stay available but folded: they are not the subject of
   the screen. */
.notes {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 0 14px;
  margin: 16px 0 0;
  background: #fcfcfd;
}
.notes > summary { padding: 11px 0; font-size: 13px; color: var(--text-2); font-weight: 500; }
.notes .facts { font-size: 13px; }
.notes p { font-size: 12.5px; line-height: 1.6; color: var(--muted); }

/* Ad layer: redirect chain and the two screenshot columns. */
.chain { list-style: none; margin: 0; padding: 0; }
.chain li {
  position: relative;
  padding: 0 0 14px 26px;
  border-left: 1px solid var(--line);
  margin-left: 7px;
}
.chain li:last-child { border-left-color: transparent; padding-bottom: 0; }
.chain li::before {
  content: "";
  position: absolute;
  left: -5px;
  top: 4px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--accent);
}
.chain .hop { font-size: 12.5px; color: var(--muted); }
.chain .hopurl {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px;
  word-break: break-all;
  color: var(--text-2);
}
.shotcols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.shotcol { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; background: #fff; }
.shotcol h4 { margin: 0; padding: 10px 12px; font-size: 12.5px; font-weight: 600; border-bottom: 1px solid var(--line-soft); color: var(--text-2); }
.shotcol .shotbox { background: #f8f9fb; cursor: zoom-in; display: block; width: 100%; border: 0; padding: 0; }
.shotcol img { display: block; width: 100%; height: 210px; object-fit: cover; object-position: top; margin: 0; border: 0; }
.shotcol .missing { padding: 26px 14px; font-size: 12.5px; color: var(--muted); text-align: center; line-height: 1.6; }

/* ---- Image viewer: light, centred, titled ------------------------- */
#shotviewer {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  color: var(--text);
  width: 74vw;
  max-width: 1000px;
  height: auto;
  max-height: 88vh;
  padding: 0;
  box-shadow: 0 24px 70px rgba(16, 24, 40, .22);
}
#shotviewer::backdrop { background: rgba(16, 24, 40, .5); }
#shotviewer .viewerbar {
  background: #fff;
  border-bottom: 1px solid var(--line);
  height: auto;
  padding: 14px 18px;
  color: var(--text);
}
#shotviewer .viewerbar span { font-size: 14px; font-weight: 600; color: var(--text); }
#shotviewer .viewerbar > div { gap: 10px; }
#shotviewer .viewerbar a, #shotviewer .viewerbar .linkbtn { color: var(--accent); font-size: 13px; }
#shotviewer .viewerimage { background: #f8f9fb; height: auto; max-height: 68vh; padding: 14px; }
#shotviewer #shotimage { border: 1px solid var(--line); border-radius: var(--r-sm); }
#shotviewer #shotmeta { color: var(--muted); }
#shotviewer .galleryarrow {
  background: #fff;
  border: 1px solid var(--line);
  color: var(--text-2);
  box-shadow: 0 2px 10px rgba(16, 24, 40, .12);
}

/* ---- Settings: menu card + repeatable group cards ------------------ */
/* The settings two-column layout is `.setwrap`, declared with the settings
   body further down. An older `.setgrid` rule lived here and matched no
   element. */
/* `.setmenu`, `.setmenuhead`, `.setmenugroup` and the menu buttons are
   defined once, with the settings body further down. An earlier copy of
   `.setmenu` and `.setmenu button` stood here and was overridden in full by
   that later block (different padding, left rule and radius), so editing it
   changed nothing on screen. */
.setmain { display: flex; flex-direction: column; gap: 14px; }
.groupcard { background: #fff; border: 1px solid var(--line); border-radius: var(--r-lg); padding: 16px 18px; }
.groupcard h3 { font-size: 14.5px; margin: 0 0 12px; font-weight: 600; }
.field { margin-bottom: 14px; }
.field > label { display: block; font-size: 12.5px; color: var(--muted); margin-bottom: 6px; }
/* `.mschip` is the live chip, used by the settings chip fields and by the
   case layer's keyword/country strips. The `.multiselect`/`.msoptions`/
   `.msrow` wrappers it once sat inside were replaced by `chipField` and
   matched nothing in the markup, so they are gone rather than left to look
   like the settings layout is defined in two places. */
.mschip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--accent-soft);
  border: 1px solid #d6e4fd;
  color: #1d4ed8;
  border-radius: var(--r-sm);
  padding: 3px 5px 3px 8px;
  font-size: 12.5px;
}
.mschip button { background: none; border: 0; color: inherit; padding: 0 2px; font-size: 14px; line-height: 1; cursor: pointer; }

.copybtn {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  color: var(--text-2);
  font-size: 12.5px;
  padding: 7px 11px;
}
.addgroup {
  width: 100%;
  border: 1px dashed #cfd4dc;
  border-radius: var(--r-lg);
  background: #fff;
  color: var(--accent);
  padding: 13px;
  font-size: 13.5px;
  cursor: pointer;
}
.addgroup:hover { border-color: var(--accent); background: #fcfdff; filter: none; }
.setfooter {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 0 0;
  border-top: 1px solid var(--line);
  margin-top: 4px;
}
.primarybtn {
  background: var(--accent);
  color: #fff;
  border: 0;
  border-radius: var(--r-md);
  padding: 9px 18px;
  font-size: 13.5px;
}

/* ---- Mobile ------------------------------------------------------- */
@media (max-width: 900px) {
  /* The narrow rail is ONE horizontal strip: fixed logo, horizontally
     scrollable nav, fixed account button. It previously rendered ~310px tall
     with the buttons scattered across pseudo-columns and the avatar clipped
     off the right edge, because two global rules fight this layout:
       - style.css `nav { flex-wrap: wrap }` applies to every nav, so the
         row wrapped instead of scrolling;
       - style.css `.navgroup { flex: 1 1 100% }` at this same breakpoint
         forces each group label onto its own full-width line, which breaks
         the row into columns.
     Overridden here rather than edited there: those rules serve the auth
     screen and the desktop rail, which are not in this task's scope.
     `aside nav` (0,0,2) and `aside .navgroup` (0,1,1) win on specificity;
     theme.css also loads after style.css. */
  aside {
    position: static;
    width: auto;
    flex-direction: row;
    align-items: center;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    /* The strip itself no longer scrolls - the nav inside it does, so the
       account button stays reachable without scrolling the whole rail. */
    overflow-x: hidden;
  }
  .rail-logo { height: 48px; padding: 0 10px; flex: 0 0 auto; }
  aside nav {
    flex-direction: row;
    flex-wrap: nowrap;
    /* Takes the space between logo and account button, and scrolls within it. */
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  aside nav::-webkit-scrollbar { display: none; }
  /* 68x44 keeps every destination at a usable touch target while scrolling. */
  aside nav button { min-width: 68px; min-height: 44px; flex: 0 0 auto; }
  /* Group headings are vertical-rail furniture. In a single scrolling line
     they can only act as row breaks, which is exactly what broke the layout;
     each button still carries its own label. */
  aside .navgroup { display: none; }
  /* `.rail-bottom` is a vertical-rail footer: in a horizontal strip its two
     navs plus the account button claimed 274px of 390px and squeezed the
     scrolling nav down to a 31px slot. It scrolls with everything else, and
     only the account button is pinned (below), so Operations, Users & Access
     and Settings sit on the same line as the rest instead of competing with
     it for width. */
  .rail-bottom { margin-top: 0; display: flex; align-items: center;
                 padding-bottom: 0; flex: 0 1 auto; min-width: 0;
                 overflow-x: auto; scrollbar-width: none; }
  .rail-bottom::-webkit-scrollbar { display: none; }
  .rail-bottom nav { flex-wrap: nowrap; }
  /* Pinned: the account menu must never scroll out of reach. */
  .rail-user { flex: 0 0 auto; margin: 0 8px 0 4px; }
  .rail-brand, aside .buildstamp { display: none; }
  main { margin-left: 0; padding: 14px 12px 26px; }
  header h1 { font-size: 21px; }
  #casedetail, #addetail { width: 100vw; }
  .fieldrow { flex-direction: column; gap: 6px; min-height: 0; }
  .fieldrow > .flabel { flex: none; padding-top: 0; }
  /* .setwrap's mobile collapse lives with its base rule further down: the
     base is declared after this block, so an override here would lose on
     source order at equal specificity. */
  .shotcols { grid-template-columns: 1fr; }
  /* The one-line filter row is a desktop decision (it keeps the table from
     being pushed down); at phone width there is no room for it, and refusing
     to wrap pushed search and the icon buttons off-screen. */
  .filterrow { flex-wrap: wrap; }
  /* Left to size themselves, the pills pack one or two per row at random and
     the filter block eats the whole first screen before a single case is
     visible. A shared basis packs them two per row, so the list starts higher
     up. Nothing is hidden: every control the desktop bar has is still here. */
  .filterrow > .fpill { flex: 1 1 calc(50% - 4px); }
  .filterrow .morechip { flex: 1 1 calc(50% - 4px); justify-content: center; }
  .filterrow .fsearch { margin-left: 0; flex: 1 1 100%; order: 1; }
  .filterrow .fsearch input { width: 100%; }
  .filterrow .iconbtn { order: 2; margin-left: 0; }
  #casebody, #adbody { padding: 8px 16px 22px; }
  .layerhead { padding: 14px 16px; }
}

/* Slim disclosure. One line by default, full text on expand. The point is that
   nothing is removed: a reader can always get the whole statement, but it stops
   costing two lines of every screen. */
.slimnotice { padding: 0; }
.slimnotice details { margin: 0; }
.slimnotice summary {
  cursor: pointer;
  list-style: none;
  padding: 8px 14px;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--text-2);
}
.slimnotice summary::-webkit-details-marker { display: none; }
.slimnotice summary::after { content: " Details"; color: var(--accent); font-size: 11.5px; }
.slimnotice details[open] summary::after { content: " Hide"; }
.slimnotice p { margin: 0; padding: 0 14px 10px; font-size: 12px; color: var(--muted); line-height: 1.55; }

/* style.css caps every .tablescroll at 340px, which is right for the small
   embedded tables it was written for but clips the case list mid-row. The case
   list is paged, so its height is bounded by pageSize, not by a scrollbox.

   Only the vertical cap is removed. An earlier `overflow: visible` here also
   disabled horizontal scrolling, which is invisible on a desktop width where
   the table fits, but on a phone the 1100px table escaped its 364px container
   and dragged the layout viewport out to 1112px - so the whole page rendered
   zoomed out and every other element was unreadable. overflow-y resolves to
   auto once overflow-x is not visible; with max-height:none there is nothing
   to scroll vertically, so no vertical scrollbar appears. */
.casepanel .tablescroll { max-height: none; overflow-x: auto; overflow-y: visible; }
.casepanel .tablescroll table { min-width: 0; }

/* Keep the pager on screen (B7 "固定底部分页").

   Measured at the 1349x722 acceptance viewport: the fixed chrome above the
   table is 275px (main padding, header, the demo-database notice, tabs,
   filters, the "N of M cases shown" line) and the pager is 52px, so an
   unbounded table put the pager at y=804 - off screen, leaving page 2 neither
   reachable nor discoverable without scrolling first.

   The reference bounds its table the same way: its own capture shows 8 of its
   10 rows with the pager resting at y~663. It simply has 94px more to spend,
   because it carries neither the demo notice nor the exclusion count - both
   of which are true statements about this data and are not dropped to win a
   pixel comparison.

   Desktop only. The rule above exists because a previous `overflow: visible`
   let a 1100px table drag the phone layout viewport out to 1112px; that fix
   is left exactly as it stands. */
@media (min-width: 901px) {
  .casepanel .tablescroll {
    max-height: calc(100vh - 347px);
    min-height: 180px;
    overflow-y: auto;
  }
  /* The header stays put while rows scroll under it; otherwise the column
     meanings are lost the moment the list moves. */
  .casepanel .tablescroll thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--panel);
  }
}

/* ---- Case layer: inline editing on the label-value rows ---------------
   Row geometry is defined once, at .fieldrow above. These are the controls
   that sit in the value column. */
.fieldlist { margin: 4px 0 0; }
.fieldrow:last-child { border-bottom: 0; }
.idchip {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  background: var(--chip);
  border: 1px solid var(--chip-line);
  border-radius: var(--r-sm);
  padding: 3px 8px;
}
.notetext { white-space: pre-wrap; }
.linkvalue { background: transparent; border: 0; padding: 0; color: var(--text); font-size: 13px; cursor: pointer; border-bottom: 1px dashed var(--line); }
.statuschip {
  border-radius: 999px;
  padding: 3px 11px;
  font-size: 12px;
  font-weight: 600;
  border: 1px solid transparent;
  cursor: pointer;
}
.s-new { background: #eef2ff; color: #3538cd; border-color: #c7d7fe; }
.s-in_progress { background: #fff6e8; color: #b54708; border-color: #fedf89; }
.s-resolved { background: #ecfdf3; color: #067647; border-color: #abefc6; }
/* Requested-only countries carry a star and a muted border: the difference
   between "we aimed here" and "the browser was verified here" must survive
   being reduced to a chip. */
.mschip.unverified { border-style: dashed; color: var(--muted); }
.savehint { margin: 8px 0 0; font-size: 12px; color: var(--muted); }
.savehint.bad { color: #b42318; }
.sectionhead {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 22px 0 8px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}
.sectionhead h3 { margin: 0; font-size: 14px; }
.detfilters { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
.detfilters .inlineinput { width: 260px; }
/* Our epistemic notes are real content, but they are not what a reviewer
   opens a case to read, so they sit below the evidence behind a summary. */
.notes { margin-top: 20px; border-top: 1px solid var(--line); padding-top: 10px; }
.notes summary { cursor: pointer; font-size: 13px; font-weight: 600; color: var(--text-2); }
.notesbody { font-size: 12.5px; color: var(--text-2); line-height: 1.6; }
.notesbody p { margin: 8px 0; }

/* Chips placed straight into a value column (no .chips wrapper) still need
   the same rhythm as the wrapped case. */
.fvalue > .mschip, .fvalue > .tagchip, .fvalue > .morechip { margin: 0 6px 0 0; }
.fvalue > .chiprest > .mschip { margin: 0 6px 0 0; }

/* A manually linked affiliate ID must not be mistakable for an observed one.
   The observed chip is the plain `.idchip`; a linked chip is dashed and
   carries the word "linked" inline, so the distinction survives a
   screenshot, a greyscale print and a colour-blind reader. */
.codeline { margin: 0 0 4px; }
.codeline:last-child { margin-bottom: 0; }
.linkchip {
  display: inline-block;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px;
  background: #fffaf0;
  border: 1px dashed #f0c88a;
  color: #8a5a10;
  border-radius: var(--r-sm);
  padding: 3px 8px;
  margin: 0 6px 0 0;
}
.linkchip b {
  font-family: var(--font, inherit);
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  margin-left: 6px;
  opacity: .85;
}

/* Detected-ads table: the reference keeps rows on one line and gives the
   title the slack, so the eye scans titles rather than wrapped columns.
   There is no Details button column -- the row itself opens the ad. */
.dettable tbody tr { cursor: pointer; }
.dettable tbody tr:hover { background: var(--row-hover); }
.dettable .titlecell { max-width: 340px; }
.dettable .titlecell .chip { margin-left: 7px; vertical-align: middle; }
.dettable .srccell, .dettable .devcell { width: 56px; text-align: center; color: var(--muted); }
.dettable .srccell svg, .dettable .devcell svg { vertical-align: middle; }
.dettable .datecell { white-space: nowrap; color: var(--text-2); }
/* Device is a glyph; the word stays in the accessibility tree. */
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ---- Redirect path -----------------------------------------------------
   A vertical chain: one dot per hop joined by a line, the URL on one line,
   and a caret that reveals what was actually recorded for that hop (status,
   method, resolved IPs). The detail is real captured data, so it is offered
   rather than summarised away. */
/* The reference frames the ad's destination URL in its own bordered box above
   the redirect path. The URL is long and must stay readable rather than being
   truncated into something that looks like a different address. */
.linkbox { display: flex; align-items: center; gap: 8px; margin: 4px 0 2px;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px;
  background: #f8fafc; }
.linkbox a { flex: 1; color: var(--accent); font-size: 12.5px; line-height: 1.5;
  word-break: break-all; text-decoration: none; }
.linkbox a:hover { text-decoration: underline; }

.hoplist { list-style: none; margin: 4px 0 0; padding: 0; }
.hop { position: relative; padding: 0 0 18px 30px; }
.hop:last-child { padding-bottom: 4px; }
.hopdot {
  position: absolute;
  left: 0;
  top: 4px;
  width: 15px;
  height: 15px;
  border: 2px solid var(--accent);
  border-radius: 50%;
  background: #fff;
}
/* The connector stops at the last hop, so the chain reads as terminated. */
.hop:not(.lasthop)::before {
  content: "";
  position: absolute;
  left: 7px;
  top: 20px;
  bottom: 2px;
  width: 1px;
  /* --accent-soft is a fill tint (#e8f0fe) and vanishes as a 1px line. */
  background: #c7d7fe;
}
.hoprow { display: flex; align-items: center; gap: 6px; min-width: 0; }
.hopcaret {
  background: none;
  border: 0;
  padding: 0 2px;
  color: var(--muted);
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
  transition: transform .12s ease;
}
.hopcaret.open { transform: rotate(180deg); }
.hopcaret.ghost { width: 14px; cursor: default; }
.hopurl {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 13px;
  color: var(--text-2);
}
.hopicon {
  flex: none;
  background: none;
  border: 0;
  padding: 2px;
  color: var(--muted);
  cursor: pointer;
  display: inline-flex;
}
.hopicon:hover { color: var(--accent); filter: none; }
.hopicon.copied { color: #067647; }
.hopdetail {
  margin: 7px 0 0;
  padding: 9px 11px;
  background: var(--chip);
  border: 1px solid var(--chip-line);
  border-radius: var(--r-sm);
  font-size: 12.5px;
  color: var(--text-2);
}
.hopdetail > div { padding: 2px 0; }
.hopdetail .hk { display: inline-block; min-width: 108px; color: var(--muted); }
.hopfull {
  margin-top: 5px;
  padding-top: 6px;
  border-top: 1px solid var(--chip-line);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11.5px;
  word-break: break-all;
  color: var(--muted);
}

/* ---- Screenshots -------------------------------------------------------
   Two columns as in the reference (landing page and SERP), each with a dark
   title bar carrying the capture time. The provenance caveats stay under the
   image: an image without its scope is the easiest thing to misread. */
.shotcols { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.shotcard {
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  background: #fff;
}
.shotbar {
  background: #1d2939;
  color: #f9fafb;
  font-size: 11px;
  padding: 5px 9px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.shotcard img { display: block; width: 100%; height: 210px; object-fit: cover; object-position: top; }
.shotfoot { padding: 9px 11px; font-size: 12px; color: var(--text-2); display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }
.shotfoot b { flex-basis: 100%; }
.shotfoot small { flex-basis: 100%; color: var(--muted); line-height: 1.5; }

/* ---- Settings: menu card + section cards ------------------------------- */
.setwrap { display: grid; grid-template-columns: 280px 1fr; gap: 22px; align-items: start; }
/* The settings view uses this grid, not the older .setgrid. Collapsed here
   rather than in the mobile block above, which is declared earlier in the
   file and would lose on source order at equal specificity. */
@media (max-width: 900px) {
  .setwrap { grid-template-columns: 1fr; gap: 14px; }
}
.setmenu {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  padding-bottom: 8px;
}
.setmenuhead {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 16px 18px;
  border-bottom: 1px solid var(--line);
  font-size: 15px;
}
.setmenugroup {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 13px 18px 9px;
  color: var(--accent);
  font-size: 13.5px;
}
.setmenu button {
  display: block;
  width: 100%;
  text-align: left;
  background: #fff;
  color: var(--text-2);
  border: 0;
  border-left: 3px solid transparent;
  border-radius: 0;
  padding: 10px 18px 10px 42px;
  font-size: 13.5px;
  cursor: pointer;
}
.setmenu button:hover { background: #f4f6f9; filter: none; }
/* The selected section. `border-left` is transparent on the base rule above
   precisely so this can fill it in without shifting the label. */
.setmenu button[aria-pressed="true"] {
  background: var(--accent-soft);
  border-left-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}
.groupcardhead { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.groupcardhead .gname {
  flex: 1 1 auto;
  /* Without min-width:0 a flex input refuses to shrink below its intrinsic
     size, so on a phone the group name ran under the Remove group button. */
  min-width: 0;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  padding: 6px 9px;
  font-size: 15px;
  font-weight: 600;
  background: transparent;
}
.groupcardhead .gname:hover { border-color: var(--line); }
.groupcardhead .gname:focus { border-color: var(--accent); background: #fff; }
.chipfield { margin-bottom: 16px; }
/* A refused country. Amber, not red: nothing is broken and nothing was lost,
   the value simply was not something the collector can request. */
.chiphint {
  margin: 6px 0 0;
  font-size: 12.5px;
  color: #92400e;
  background: #fffbeb;
  border: 1px solid #fde68a;
  border-radius: var(--r-sm);
  padding: 6px 9px;
}
.chipfieldhead { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.chipfieldhead h4 { margin: 0 0 2px; font-size: 13.5px; font-weight: 600; }
.chipfieldhead p { margin: 0 0 8px; font-size: 12.5px; color: var(--muted); }
.copybtn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: #fff;
  color: var(--text-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 5px 10px;
  font-size: 12.5px;
  cursor: pointer;
  flex: none;
}
.copybtn:hover { background: #f4f6f9; filter: none; }
.copybtn.copied { color: #067647; border-color: #abefc6; }
/* The box is the control: chips and the entry field share one bordered
   surface, so adding a value reads as extending the list. */
.chipbox {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 8px 10px;
  background: #fff;
  min-height: 42px;
}
.chipbox:focus-within { border-color: var(--accent); }
/* The 160px floor keeps the placeholder readable when the input shares a row
   with chips. On a phone that floor is wider than the space left beside a
   chip, so the placeholder was cut off mid-word; there it takes its own row
   instead. The floor is kept above that width, where it does no harm. */
.chipinput { flex: 1 1 160px; border: 0; padding: 3px 2px; font-size: 13px; min-width: 160px; background: transparent; }
@media (max-width: 900px) {
  .chipinput { flex-basis: 100%; min-width: 0; }
}
.chipinput:focus { outline: none; }
.chipx {
  background: none;
  border: 0;
  color: var(--muted);
  cursor: pointer;
  padding: 0 0 0 5px;
  font-size: 14px;
  line-height: 1;
}
.chipx:hover { color: #b42318; filter: none; }
.groupfoot {
  display: flex;
  justify-content: flex-end;
  gap: 26px;
  padding-top: 12px;
  margin-top: 4px;
  border-top: 1px solid var(--line-soft);
  font-size: 13px;
  color: var(--muted);
}
.groupfoot b { color: var(--text); margin-left: 4px; }
.cardbasis { margin: 8px 0 0; font-size: 12px; color: var(--muted); }
.addgroup {
  width: 100%;
  background: #fff;
  color: var(--accent);
  border: 1px dashed #b6c9f4;
  border-radius: var(--r-lg);
  padding: 14px;
  font-size: 13.5px;
  cursor: pointer;
}
.addgroup:hover { background: var(--accent-soft); filter: none; }
/* The reference shows a monthly price here. This workspace has no pricing
   model, so the same slot carries the honest equivalent: how many
   keyword-country combinations the settings ask for. */
.totalcard {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 16px 18px;
  font-size: 15px;
}
.setfoot { display: flex; align-items: center; justify-content: flex-end; gap: 14px; }

/* A two-word action must not wrap into the card title beside it. */
.nowrapbtn { white-space: nowrap; flex: none; }

.groupcardhead h3 { margin: 0; font-size: 15px; font-weight: 600; flex: 1 1 auto; }
.cardlead { margin: -6px 0 14px; font-size: 12.5px; color: var(--muted); }
.settingsgrid { margin-bottom: 14px; }
.settingsgrid input { border: 1px solid var(--line); border-radius: var(--r-md); padding: 7px 9px; font-size: 13px; width: 100%; }
.settingsgrid input:focus { border-color: var(--accent); outline: none; }
.checkline { display: flex; align-items: center; gap: 8px; font-size: 13px; margin: 12px 0 0; }
.checkline input { width: auto; }
/* Confirmation after a save: the write already happened, so this states the
   consequence (lists rebuilt) rather than just "OK". */
.savedflash {
  margin: 0 0 14px;
  padding: 10px 14px;
  border-radius: var(--r-md);
  background: #ecfdf3;
  border: 1px solid #abefc6;
  color: #067647;
  font-size: 13px;
}

/* ---- Legacy palette mapping -------------------------------------------
   style.css predates the theme and paints links, cards, focus rings and
   counters in a teal family (#087d79 / #0f5f5c / #149b89). Those rules are
   still live on the Evidence view, so the same product showed two accent
   colours depending on which view you opened. Rather than edit twenty
   scattered declarations in a minified stylesheet, the surviving classes
   are mapped onto the theme accent here. */
.linkbtn:hover { color: var(--accent); border-bottom-color: var(--accent); }
.evidencezoom { color: var(--accent); }
.evidencezoom:hover { border-color: var(--accent); }
.sortbtn:hover, .sortbtn.on { color: var(--accent); }
.casetable tbody tr:focus { outline-color: var(--accent); }
.modeswitch button[aria-pressed="true"] { background: var(--accent); color: #fff; }
button:focus-visible, a:focus-visible, summary:focus-visible { outline-color: var(--accent); }

/* The base pill is green, and `.pill.state-new` never had a rule of its own,
   so a NEW case rendered green in the legacy views while the case list paints
   the same state blue. One state must not have two colours. Neutral is the
   default; `new` matches the case-list status chip. */
.pill { background: var(--chip); color: var(--text-2); }
.pill.state-new { background: #eef4ff; border-color: #c7d7fe; color: #1d4ed8; }

/* The collection progress bar is the last teal fill. */
/* `.reason` and `.tinytag` are slate-blue chips from the old palette; they
   read as a third accent next to the theme's neutral chip. */
.reason, .tinytag { background: var(--chip); border-color: var(--chip-line); color: var(--text-2); }

/* ---- Loading and failure states ---------------------------------------
   A slow request used to show nothing, so the view looked empty - which is
   the same thing the UI shows for "no cases". The two must never look alike:
   one is a gap in knowledge, the other is a finding. */
.skeleton { display: flex; flex-direction: column; gap: 10px; padding: 4px 0; }
.skelrow {
  height: 46px;
  border-radius: var(--r-md);
  background: linear-gradient(90deg, #f1f3f6 25%, #e8ebf0 37%, #f1f3f6 63%);
  background-size: 400% 100%;
  animation: skelshine 1.3s ease-in-out infinite;
}
@keyframes skelshine { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }
@media (prefers-reduced-motion: reduce) { .skelrow { animation: none; } }
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.loaderror { border-color: #f3c6c6; background: #fffafa; }
.retrybtn { margin-top: 12px; align-self: flex-start; }

/* ---- Concept pages (Ad Radar / AI Radar) ------------------------------
   Built on the reference's template: title chip, dark hero, Explore grid.
   The reference's hero CTA is a sales action ("Contact Us For Access");
   there is nothing to sell here, so the hero states what is missing
   instead of offering access that does not exist. */
.titlerow { display: flex; align-items: center; gap: 10px; }
.titlechip {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--line);
  background: var(--chip);
  color: var(--text-2);
  border-radius: var(--r-sm);
  padding: 3px 9px;
  font-size: 12px;
  font-weight: 500;
}
.conceptwrap { display: flex; flex-direction: column; gap: 18px; }
.concepthero {
  background: linear-gradient(120deg, #0f1b2d 0%, #16283f 55%, #1d3b5c 100%);
  background-color: #0f1b2d;
  border-radius: var(--r-lg);
  padding: 34px 38px;
  color: #fff;
}
.conceptheroinner { max-width: 640px; }
.concepthero h2 { margin: 0 0 10px; font-size: 26px; font-weight: 600; color: #fff; }
.concepthero p { margin: 0; font-size: 14px; line-height: 1.65; color: #c9d4e3; }
.concepthero .heronote {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, .14);
  font-size: 12.5px;
  color: #9fb0c6;
}
.exploreh { margin: 0 0 18px; font-size: 17px; font-weight: 600; }
.exploregrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; }
.explorecard {
  display: flex;
  gap: 12px;
  padding: 14px 22px 14px 0;
  border-right: 1px solid var(--line-soft);
}
.explorecard:nth-child(3n) { border-right: 0; }
.explorecard:not(:nth-child(-n+3)) { border-top: 1px solid var(--line-soft); padding-top: 20px; }
.explorecard b { display: block; font-size: 13.5px; margin-bottom: 3px; }
.explorecard small { display: block; font-size: 12.5px; color: var(--muted); line-height: 1.55; }
.exploreicon {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.tint-rose { background: #fff1f3; color: #c11574; }
.tint-blue { background: #eff8ff; color: #175cd3; }
.tint-amber { background: #fffaeb; color: #b54708; }
.tint-green { background: #ecfdf3; color: #067647; }
.tint-cyan { background: #ecfeff; color: #0e7090; }
.tint-violet { background: #f4f3ff; color: #5925dc; }
@media (max-width: 900px) {
  .exploregrid { grid-template-columns: 1fr; }
  .explorecard { border-right: 0; padding-right: 0; }
  .explorecard:not(:first-child) { border-top: 1px solid var(--line-soft); padding-top: 18px; }
}

/* Transparency captures that attach to no observation row. Shown as cards in
   their own panel rather than as table rows: they are not part of any case
   list, and rendering them in the advertiser table would imply they are. */
.transcap small.warn { color: #8a5a12; }

/* ---- Platform screens (Operations, Users & Access) --------------------
   Only the classes these two views introduce. Everything else reuses the
   existing panel/metric/table/pill vocabulary so the platform surface does
   not drift into a second visual language. */
.small { font-size: 12.5px; line-height: 1.5; }
.grouprow { display: flex; justify-content: space-between; align-items: flex-start;
  gap: 16px; padding: 14px 0; border-bottom: 1px solid #e6edf0; }
.grouprow:last-of-type { border-bottom: 0; }
.groupactions { display: flex; gap: 8px; flex-shrink: 0; flex-wrap: wrap; }
.groupactions button { padding: 8px 13px; font-size: 13px; }
.groupactions button.secondary { background: white; color: #33505f; border: 1px solid #cbd6dc; }
.groupactions button.secondary:hover { border-color: var(--accent); color: var(--accent); filter: none; }
.groupactions button[disabled] { opacity: .45; cursor: not-allowed; filter: none; }
.formgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; }
.formgrid form { display: flex; flex-direction: column; gap: 10px; }
.formgrid h3 { margin: 0; font-size: 15px; }
.formgrid label { display: flex; flex-direction: column; gap: 5px; font-size: 13.5px; }
.formgrid select[multiple] { padding: 6px; }
/* The collector log is machine output: preserve its line structure, and keep
   a long line from widening the whole page. */
.logbox { background: #10222c; color: #d7e6ec; padding: 14px; border-radius: 8px;
  font-size: 12px; line-height: 1.55; max-height: 340px; overflow: auto;
  white-space: pre-wrap; word-break: break-word; }
@media (max-width: 760px) {
  .formgrid { grid-template-columns: 1fr; }
  .grouprow { flex-direction: column; }
}

/* ---- Roster ---------------------------------------------------------
   `.panel` is padding:0 by design so list panels can run tables edge to
   edge, and padding is restored only on `.panel > h2`. Every other direct
   child therefore sits against the border. The roster's panels are prose
   and forms, not full-bleed tables, so they opt back in here rather than
   by changing `.panel` and reflowing every table panel in the product. */
#roster .panel { padding: 20px; }
#roster .panel > h2 { padding: 0 0 4px; }
#roster .panelhead { display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; margin-bottom: 8px; }
#roster .panelhead h2 { margin: 0; }
#roster .panel > p.muted { margin-top: 0; }
#roster table { width: 100%; }

/* The declare-complete control and the consequence of using it, on one
   row: the sentence explains the button beside it, not a setting on some
   other screen. */
.rosdeclare { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; border: 1px solid var(--line); border-radius: 10px;
  padding: 14px 16px; background: #f8fafb; margin-top: 14px; }
.rosdeclare p { margin: 4px 0 0; }
.rosdeclare button { white-space: nowrap; flex: 0 0 auto; }

/* File input and its action share a baseline; the input is allowed to
   shrink so the button never leaves the card on a narrow screen. */
.rosfilerow { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rosfilerow input[type=file] { flex: 1 1 260px; min-width: 0; }
.rosfilerow button { flex: 0 0 auto; }

.rosformat { border: 1px solid var(--line); border-radius: 10px; padding: 0 14px;
  margin-top: 14px; background: #fff; }
.rosformat summary { cursor: pointer; padding: 12px 0; font-size: 14px; font-weight: 600; }
.rosformat > div { padding-bottom: 14px; }
.rosformat code { background: #eef3f5; border-radius: 4px; padding: 1px 5px; font-size: 12px; }
.rosformat .button { display: inline-block; margin-top: 6px; }

@media (max-width: 760px) {
  #roster .panel { padding: 16px; }
  .rosdeclare { flex-direction: column; align-items: stretch; }
  .rosdeclare button { width: 100%; }
  .rosfilerow { flex-direction: column; align-items: stretch; }
  /* Column direction makes `flex: 1 1 260px` a 260px MINIMUM HEIGHT, which
     rendered the file input as a tall empty box. Reset both axes here. */
  .rosfilerow input[type=file] { flex: 0 0 auto; width: 100%; }
  .rosfilerow button { width: 100%; }
}

/* One row per observed platform account in a hijacking case. */
.caseaccounts { display: grid; gap: 12px; }
.caseaccount { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.caseaccount .accountid { overflow-wrap: anywhere; white-space: normal; }
.accountdisclosures { display: flex; flex-wrap: wrap; gap: 6px; }

/* ---- Platform screens: Operations, Users & Access --------------------
   style.css sets `td { padding: 18px 16px }` and `th { padding: 16px }` for
   the evidence tables, where a row is a screenshot and a paragraph. An
   operations row is a region and a status, and at that padding six sources
   filled a 5250px-tall page. These rules are scoped to the two platform
   sections so the evidence tables keep their own spacing. */
#operations .panel, #users .panel { padding: 18px 20px; }
#operations h2, #users h2 { font-size: 16px; margin: 0; }
#operations .panelhead, #users .panelhead {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 6px;
}
#operations .panel > .muted.small { margin: 0 0 12px; }

.opstable { width: 100%; border-collapse: collapse; font-size: 13px; }
.opstable th {
  background: #f8fafb; color: var(--muted); padding: 8px 10px;
  font-size: 11.5px; letter-spacing: .3px; text-transform: uppercase;
  white-space: nowrap; border-bottom: 1px solid var(--line);
}
.opstable td {
  padding: 9px 10px; border-top: 1px solid #eef2f5; vertical-align: top;
  max-width: 280px; overflow-wrap: anywhere; line-height: 1.45;
}
.opstable td .small, .opstable td .muted { font-size: 12px; }
.opstable tbody tr.rowwarn { background: #fffaf3; }
/* A three-column directory stretched across a 1300px panel put 600px of
   nothing between "IM8" and "im8". These tables size to their content and
   stop, with a cap so a long brand name still wraps rather than pushing the
   panel wide. */
.opstable.narrow { width: auto; min-width: min(100%, 560px); max-width: 100%; }
.opstable.narrow th, .opstable.narrow td { padding-right: 34px; }
.opstable.narrow th:last-child, .opstable.narrow td:last-child { padding-right: 10px; }
.opstable.narrow td:first-child { white-space: nowrap; }
/* `overflow-wrap: anywhere` is right for an advertiser URL and wrong for a
   role name: a squeezed column rendered "Platform operator" as "Platfo / rm /
   opera / tor". The directory columns hold prose, so they break on spaces. */
.opstable.narrow td { overflow-wrap: break-word; }
.opstable code { font-size: 12px; }

/* Summary cards. `.metrics`/`.metric` are the evidence dashboard's 30px
   figure tiles; a status word is not a figure and does not need that size. */
.opscards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px; margin-bottom: 14px;
}
.opscard {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 13px 15px;
  display: flex; flex-direction: column; gap: 3px;
}
.opscard small { font-size: 11.5px; letter-spacing: .3px; text-transform: uppercase; color: var(--muted); }
.opscard strong { font-size: 19px; font-weight: 620; line-height: 1.25; }
.opscard .small { font-size: 12px; line-height: 1.45; }
.tone-warn { color: #b45309; }
.tone-good { color: #15803d; }

.opsline { font-size: 12.5px; color: var(--text-2); margin: 0 0 10px; line-height: 1.6; }
.opsline code { font-size: 12px; }
.opsline .pill { margin-left: 4px; }

/* One alert style: a box that is only drawn when something is actually
   wrong, so its presence carries meaning. */
.opsalert {
  background: #fff7ed; border: 1px solid #fed7aa; border-radius: var(--r-md);
  padding: 10px 13px; margin: 0 0 12px; font-size: 13px; line-height: 1.55;
}
.opsalert b { display: block; color: #9a3412; }
.opsalert span { color: var(--text-2); }

/* Slim disclosure: one line by default, the full technical record on
   expand. Nothing true is removed, it just stops costing a screen. */
.opsnote { font-size: 12.5px; margin: 0 0 14px; }
.opsnote summary { cursor: pointer; color: var(--text-2); font-weight: 500; font-size: 12.5px; }
.opsnote > div { padding: 6px 0 0; color: var(--muted); line-height: 1.6; }
.opsnote p { margin: 0 0 6px; }
.opsrefresh { font-size: 12px; color: var(--muted); margin: 0 0 10px; }

details.opsdetails { padding: 14px 20px; }
details.opsdetails > summary { font-size: 14px; font-weight: 600; }
details.opsdetails[open] > summary { margin-bottom: 12px; }
details.opsdetails summary .muted { font-weight: 400; font-size: 12.5px; }

/* Action feedback sits next to the controls and is coloured by outcome:
   a refusal must not read like a confirmation. */
.opsmsg { font-size: 12.5px; margin: 10px 0 0; min-height: 1.2em; color: var(--muted); }
.opsmsg[data-tone="ok"] { color: #15803d; }
.opsmsg[data-tone="error"] { color: #b42318; }
#operations .grouprow { padding: 11px 0; }
#operations .grouprow .pill { margin-left: 6px; }
.groupactions button[aria-busy="true"] { opacity: .6; }

/* Users & Access */
.rolecards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 10px; margin-top: 14px;
}
.rolecard {
  border: 1px solid var(--line); border-radius: var(--r-md); padding: 10px 12px;
  display: flex; flex-direction: column; gap: 3px; font-size: 13px;
}
.rolecard .small { line-height: 1.5; }

.usform {
  border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 14px 16px; margin-bottom: 14px; background: #fbfdfe;
}
/* `align-items: start` is stated because the generic `.formrow` rule centres
   its items: with Password one helper line taller than its neighbours, the
   Username and Role labels were pushed 20px below it. */
.usform .formrow {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px;
  align-items: start;
}
/* Top-aligned: the three fields in a row are different heights (Password
   carries a helper line), and a stretched flex column would otherwise show
   the shorter labels lower than the taller one. */
.usform label { display: flex; flex-direction: column; justify-content: flex-start; gap: 4px; font-size: 13px; }
.usform input, .usform select { padding: 8px 10px; font-size: 13.5px; }
.usform .brandpick { border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 10px 12px; margin: 14px 0 0; }
.usform .brandpick legend { font-size: 12px; color: var(--muted); padding: 0 4px; }
/* `auto-fit` with one brand stretched the single cell across the fieldset and
   the checkbox sat centred in it, so `auto-fill` keeps the cell at column
   width and the boxes start at the left edge. */
.checkgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 6px; }
/* `.usform label` above is a column; a checkbox and its caption are one row.
   Same specificity as `label.checkline` in style.css, but this sheet loads
   later, so the row direction has to be restated here. */
.usform .checkline, .checkgrid .checkline {
  flex-direction: row; align-items: center; justify-content: flex-start;
  gap: 8px; margin: 0; font-size: 13px;
}
.usform .checkline input { width: auto; margin: 0; }
/* A form's submit is a button, not a banner: full-width primary buttons made
   every creation form look like the page's main action. */
.usform .formactions { display: flex; align-items: center; gap: 12px; margin-top: 14px; flex-wrap: wrap; }
.usform .formactions button { flex: 0 0 auto; }
/* The account maintenance panel opens as a row under the account it edits,
   so the operator's eye does not leave the row they were reading. */
.opstable tbody tr.rowoff td { color: var(--muted); }
.opstable tbody tr.rowoff td b { text-decoration: line-through; text-decoration-color: var(--muted); }
.opstable td.rowact { text-align: right; white-space: nowrap; }
.opstable tbody tr.editrow > td { padding: 0 0 12px; background: #fbfdfe; }
.usedit { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px;
  padding: 12px 12px 0; border-left: 3px solid var(--accent-soft); }
.usedit .usform { margin: 0; }
.usedit h4 { margin: 0 0 8px; font-size: 13.5px; font-weight: 600; }
.usedit .formactions { margin-top: 10px; }
.usedit button[disabled] { opacity: .45; cursor: not-allowed; }

@media (max-width: 760px) {
  #operations .panel, #users .panel { padding: 14px; }
  .opscards { grid-template-columns: 1fr 1fr; gap: 10px; }
  .opscard strong { font-size: 17px; }
  #operations .panelhead, #users .panelhead { align-items: flex-start; }
  .opstable { font-size: 12.5px; }
  .opstable td { padding: 8px; }
  /* On a phone the 560px floor and the 34px desktop gutters pushed the
     directory past the viewport and scrolled the whole document sideways.
     Content width wins over column rhythm at this size. */
  .opstable.narrow { min-width: 0; }
  .opstable.narrow th, .opstable.narrow td { padding-right: 10px; }
  .opstable.narrow td:first-child { white-space: normal; }
  /* A fourth column for Edit pushed the account table 83px past a 390px
     screen. The table is display:block here so the action cell can drop
     under the row instead of beside it. */
  #ustable, #ustable tbody, #ustable tr:not(.editrow), #ustable td { display: block; width: 100%; }
  #ustable thead { display: none; }
  #ustable tr:not(.editrow) { border-bottom: 1px solid var(--line); padding: 6px 0; }
  #ustable td { padding: 3px 8px; border: 0; }
  #ustable td.rowact { text-align: left; white-space: normal; }
  #ustable tr.editrow > td { display: block; }
  .usedit { grid-template-columns: 1fr; }
  /* The six-column source table inherits a 560px floor, which is ~93px per
     column and wraps "configuration" as "configurati / on". It already sits
     in a horizontal scroller, so let it be as wide as it needs and scroll. */
  .tablescroll .opstable { min-width: 700px; }
  .tablescroll .opstable.narrow { min-width: 0; }
}
