/* ===================================================================
   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: #f6f8fb;
  --panel: #ffffff;
  --panel-alt: #fbfcfe;
  --line: #e3e8f0;
  --line-soft: #edf2f8;
  --text: #0b1730;
  --text-2: #33455c;
  --muted: #5f6d82;
  --accent: #1d54d1;
  --accent-2: #3b82f6;
  --accent-soft: #e8effd;
  --accent-line: #1d54d1;
  --teal: #5eead4;
  --teal-soft: #e3fbf5;
  --ink: #070f22;
  --ink-2: #0c1a36;
  --ink-3: #132a52;
  --success: #0f9d58;
  --success-soft: #e6f7ed;
  --warn: #a95c00;
  --warn-soft: #fff4e5;
  --danger: #b42318;
  --danger-soft: #fdeaea;
  --chip: #f2f4f9;
  --chip-line: #e4e8f2;
  --row-hover: #f4f7fd;
  --r-sm: 6px;
  --r-md: 8px;
  --r-lg: 12px;
  --r-xl: 18px;
  --shadow-xs: 0 1px 2px rgba(11, 23, 48, .05);
  --shadow-sm: 0 1px 2px rgba(11, 23, 48, .04), 0 6px 20px -12px rgba(11, 23, 48, .18);
  --shadow-md: 0 2px 4px rgba(11, 23, 48, .05), 0 18px 40px -18px rgba(11, 23, 48, .28);
  --shadow-lg: 0 24px 60px -20px rgba(7, 15, 34, .35);
  --ease: cubic-bezier(.2, .7, .2, 1);
  --dur: .18s;
  --rail: 232px;
}

html, body { background: var(--bg); color: var(--text); }
body { font-size: 14px; line-height: 1.5; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }

/* ---- Buttons -------------------------------------------------------
   Bare <button> is the primary action everywhere (login submit, wizard
   Next/Finish, dialog primary actions). Every other button-like class
   (.secondary, .ghostbtn, .iconbtn, .linkbtn, .sortbtn, ...) already sets
   its own background, so it never inherits the primary fill. */
button, .button {
  background: linear-gradient(180deg, var(--accent-2) 0%, var(--accent) 100%);
  color: #fff;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  padding: 9px 15px;
  font-size: 13.5px;
  cursor: pointer;
  transition: filter var(--dur) var(--ease), transform var(--dur) var(--ease),
    background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
button:hover, .button:hover { filter: brightness(1.06); }
button:active, .button:active { transform: translateY(.5px); }
button:disabled { opacity: .45; cursor: default; filter: none; transform: none; }
button:focus-visible, .button:focus-visible, a:focus-visible, summary:focus-visible,
input:focus-visible, select:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
.secondary, .ghostbtn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--panel);
  color: var(--text-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 9px 14px;
  font-size: 13.5px;
  box-shadow: var(--shadow-xs);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.secondary:hover, .ghostbtn:hover { border-color: #c7d0e0; background: var(--panel-alt); filter: none; box-shadow: var(--shadow-sm); }

/* ---- Icon rail ---------------------------------------------------- */
aside {
  position: fixed;
  inset: 0 auto 0 0;
  width: var(--rail);
  background: linear-gradient(190deg, var(--ink-2) 0%, var(--ink) 100%);
  border-right: 1px solid rgba(255, 255, 255, .06);
  color: rgba(255, 255, 255, .7);
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  z-index: 30;
}
.rail-logo {
  height: 58px;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 18px;
  margin-bottom: 10px;
  color: #fff;
  font-weight: 800;
  font-size: 14.5px;
  border-bottom: 1px solid rgba(255, 255, 255, .08);
}
.rail-logo svg { width: 21px; height: 21px; color: #fff; flex: none; }
/* The workspace control sits directly under the logo. For a Superadmin or a
   Brand owner it is a labelled indicator (one accessible brand, nothing to
   switch to). For an Agency -- which can be granted more than one brand and
   is the only role that can register a new one -- it is a real dropdown:
   the brands it can reach, plus "+ Onboard new brand". */
.rail-workspace {
  display: flex;
  align-items: center;
  gap: 8px;
  width: calc(100% - 20px);
  margin: 2px auto 12px;
  padding: 9px 10px;
  background: rgba(255, 255, 255, .05);
  border: 1px solid rgba(255, 255, 255, .1);
  border-radius: var(--r-md);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.rail-workspace:hover { border-color: rgba(255, 255, 255, .22); background: rgba(255, 255, 255, .09); }
.rail-workspace svg:first-child { width: 16px; height: 16px; flex: none; color: var(--teal); }
.rail-workspace-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-workspace-chevron { width: 14px; height: 14px; flex: none; color: rgba(255, 255, 255, .5); transition: transform var(--dur) var(--ease); }
.rail-workspace[aria-expanded="true"] .rail-workspace-chevron { transform: rotate(180deg); }
.workspacemenu {
  position: fixed;
  z-index: 60;
  width: 248px;
  background: rgba(255, 255, 255, .98);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  padding: 6px;
}
.workspacemenu-list { display: flex; flex-direction: column; gap: 1px; margin-bottom: 2px; }
.workspacemenu-item {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 9px; border-radius: var(--r-md);
  font-size: 13px; color: var(--text-2);
}
.workspacemenu-item.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.workspacemenu-item .wchip {
  font-size: 10.5px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--muted); background: var(--chip); border-radius: 4px; padding: 1px 5px;
}
.workspacemenu-empty { padding: 8px 9px; font-size: 12.5px; color: var(--muted); }
.workspacemenu-add {
  display: block; width: 100%; text-align: left; background: transparent;
  border: 0; border-top: 1px solid var(--line-soft); border-radius: 0;
  padding: 9px 9px 8px; margin-top: 2px; font-size: 13px; font-weight: 600;
  color: var(--accent); cursor: pointer; transition: background var(--dur) var(--ease);
}
.workspacemenu-add:hover { background: var(--accent-soft); filter: none; }
/* ---- Brand onboarding wizard: segmented step indicator -------------- */
#onboarddialog { padding: 0; width: min(560px, 94vw); max-width: 560px; border: 0; border-radius: var(--r-xl); box-shadow: var(--shadow-lg); }
.onboardbody { padding: 4px 28px 28px; }
.onboardsteps {
  display: flex;
  padding: 24px 28px 20px;
  margin: 0 -28px 20px;
  border-bottom: 1px solid var(--line-soft);
}
.onboardstep {
  flex: 1 1 0;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  text-align: center;
}
.onboardstep::before {
  content: attr(data-step);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--chip);
  color: var(--muted);
  font-size: 12px;
  font-weight: 700;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.onboardstep::after {
  content: "";
  position: absolute;
  top: 12px;
  left: calc(50% + 20px);
  right: calc(-50% + 20px);
  height: 2px;
  background: var(--line);
  z-index: -1;
  transition: background var(--dur) var(--ease);
}
.onboardstep:last-child::after { display: none; }
.onboardstep.active { color: var(--accent); }
.onboardstep.active::before { background: var(--accent); color: #fff; box-shadow: 0 0 0 4px var(--accent-soft); }
.onboardstep:has(~ .onboardstep.active) { color: var(--text-2); }
.onboardstep:has(~ .onboardstep.active)::before { background: var(--accent); color: #fff; }
.onboardstep:has(~ .onboardstep.active)::after { background: var(--accent); }
.onboardpane label { display: block; font-size: 12.5px; font-weight: 600; color: var(--muted); margin-bottom: 14px; }
.onboardpane input {
  width: 100%; margin-top: 6px; padding: 9px 11px; font-size: 14px;
  border: 1px solid var(--line); border-radius: var(--r-md);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.onboardpane input:focus { border-color: var(--accent-2); outline: none; box-shadow: 0 0 0 3px var(--accent-soft); }
.onboardpane .formactions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin-top: 16px; }
#obcandidates { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
#obcandidates .checkline { font-size: 13.5px; padding: 6px 0; }
.obkeywords { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; min-height: 32px; }
.obaddkw { display: flex; gap: 8px; margin-bottom: 6px; }
.obaddkw input { flex: 1 1 auto; margin-top: 0; padding: 8px 10px; font-size: 13px; border: 1px solid var(--line); border-radius: var(--r-md); }
.obaddkw button { flex: none; }
aside nav { display: flex; flex-direction: column; gap: 2px; padding: 4px 10px; }
aside nav button {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 11px;
  background: transparent;
  border: 0;
  border-radius: var(--r-md);
  padding: 9px 11px;
  color: rgba(255, 255, 255, .62);
  font-size: 13.5px;
  font-weight: 500;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
aside nav button svg { width: 18px; height: 18px; flex: none; }
aside nav button span { display: block; overflow: hidden; text-overflow: ellipsis; }
aside nav button:hover { background: rgba(255, 255, 255, .07); color: #fff; }
aside nav button.active {
  background: rgba(59, 130, 246, .2);
  color: #fff;
  font-weight: 600;
  box-shadow: inset 3px 0 0 var(--accent-2);
}
aside nav button.active svg { color: var(--teal); }

.rail-bottom { margin-top: auto; padding: 10px 12px 14px; border-top: 1px solid rgba(255, 255, 255, .08); }
.rail-user {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 8px 0 0;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent-2), var(--accent));
  color: #fff;
  border: 0;
  padding: 0;
  cursor: pointer;
  box-shadow: 0 4px 14px -4px rgba(29, 84, 209, .65);
  transition: transform var(--dur) var(--ease), filter var(--dur) var(--ease);
}
.rail-user:hover { transform: translateY(-1px); filter: brightness(1.08); }
.rail-user svg { width: 18px; height: 18px; }
aside .buildstamp {
  display: block;
  font-size: 10px;
  text-align: left;
  color: rgba(255, 255, 255, .32);
  padding: 0 2px 8px;
  word-break: break-all;
  line-height: 1.3;
}

/* The account menu is a floating light card whose DOM parent is the dark
   rail; `.accountmenu button{color:inherit}` (style.css) would otherwise
   inherit the rail's light text colour and render invisible white-on-white
   labels. Re-asserted here, plus the same blur/shadow treatment as the
   workspace menu. */
.accountmenu {
  color: var(--text-2);
  background: rgba(255, 255, 255, .98);
  -webkit-backdrop-filter: blur(20px);
  backdrop-filter: blur(20px);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
}
.accountmenu button:hover { background: var(--chip); }

/* ---- Page frame --------------------------------------------------- */
main {
  margin-left: var(--rail);
  padding: 28px 40px 40px;
  max-width: none;
}
header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 0 0 20px;
}
header h1 {
  font-size: 25px;
  font-weight: 700;
  letter-spacing: -.5px;
  margin: 0;
  color: var(--text);
}
.headeractions { display: flex; align-items: center; gap: 10px; }
.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: var(--panel-alt);
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent-2);
  border-radius: var(--r-md);
  padding: 10px 14px;
  margin-bottom: 14px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--text-2);
}
#alert:not([hidden]) { border-left-color: var(--warn); background: var(--warn-soft); }

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 0;
  margin-bottom: 16px;
  box-shadow: var(--shadow-xs);
}
.panel > h2 { padding: 18px 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;
  font-weight: 500;
  padding: 13px 12px 11px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.tabrow button:hover { color: var(--text-2); filter: none; background: transparent; }
.tabrow button[aria-pressed="true"] { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
.tabrow .tabcount {
  background: var(--chip);
  color: var(--muted);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  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: var(--panel) 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;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.filterbar input[type="date"] { background-image: none; padding-right: 10px; }
.filterbar select:hover { border-color: #cfd4dc; }
.filterbar select:focus, .filterbar input[type="date"]:focus {
  outline: none;
  border-color: var(--accent-2);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
/* 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;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.filterbar .fsearch input:focus {
  outline: none;
  border-color: var(--accent-2);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.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 {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--panel);
  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: 600;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .02em;
  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: var(--row-hover); }
.casetable tbody tr:focus { outline: 2px solid var(--accent-2); outline-offset: -2px; }
.casetable .dimrow { background: var(--panel-alt); color: var(--muted); }
.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: 999px;
  padding: 3px 10px;
  font-size: 12px;
  font-weight: 500;
  white-space: nowrap;
  background: var(--accent-soft);
  color: var(--accent);
}
.statuschip.s-investigating { background: var(--warn-soft); color: var(--warn); }
.statuschip.s-ready_for_action { background: #ecf3ff; color: #1d4ed8; }
.statuschip.s-resolved { background: var(--success-soft); color: var(--success); }
.statuschip.s-monitoring { background: var(--chip); color: var(--muted); }
.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;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.pagenums button:hover:not(:disabled) { background: var(--chip); 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; }

/* ---- Dialog foundations: rounded, layered shadow, blurred backdrop --- */
dialog::backdrop {
  background: rgba(7, 15, 34, .5);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
#detail, #invdetail {
  border: 0;
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
}

/* ---- Case layer: right overlay, not a centred modal ---------------- */
#casedetail, #addetail {
  position: fixed;
  margin: 0;
  border: 0;
  border-radius: var(--r-xl) 0 0 var(--r-xl);
  padding: 0;
  top: 0;
  bottom: 0;
  right: 0;
  left: auto;
  height: 100%;
  max-height: none;
  max-width: none;
  background: var(--panel);
  box-shadow: -24px 0 60px -16px rgba(7, 15, 34, .32), -1px 0 0 rgba(7, 15, 34, .05);
}
/* 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(7, 15, 34, .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: var(--panel);
  color: var(--text-2);
  display: grid;
  place-items: center;
  padding: 0;
  flex: none;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.iconbtn:hover { background: var(--chip); color: var(--text); filter: none; }
.iconbtn:active { transform: scale(.94); }
.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: var(--panel);
  border: 0;
  border-radius: var(--r-xl);
  color: var(--text);
  width: 74vw;
  max-width: 1000px;
  height: auto;
  max-height: 88vh;
  padding: 0;
  box-shadow: var(--shadow-lg);
}
#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 pages: shared group-card vocabulary -------------------- */
.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 #c7d7fe;
  color: var(--accent);
  border-radius: 999px;
  padding: 4px 6px 4px 10px;
  font-size: 12.5px;
  font-weight: 500;
}
.mschip button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  background: none;
  border: 0;
  border-radius: 50%;
  color: inherit;
  padding: 0;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur) var(--ease);
}
.mschip button:hover { background: rgba(29, 84, 209, .16); filter: none; }

.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: linear-gradient(180deg, var(--accent-2) 0%, var(--accent) 100%);
  color: #fff;
  border: 0;
  border-radius: var(--r-md);
  padding: 9px 18px;
  font-size: 13.5px;
  transition: filter var(--dur) var(--ease);
}
.primarybtn:hover { filter: brightness(1.06); }

/* ---- 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 rgba(255, 255, 255, .08);
    /* 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; border-bottom: 0; margin-bottom: 0; }
  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-workspace, aside .buildstamp { display: none; }
  main { margin-left: 0; padding: 14px 12px 26px; }
  header h1 { font-size: 21px; }
  #casedetail, #addetail { width: 100vw; border-radius: 0; }
  .fieldrow { flex-direction: column; gap: 6px; min-height: 0; }
  .fieldrow > .flabel { flex: none; padding-top: 0; }
  .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;
  transition: filter var(--dur) var(--ease);
}
.s-new { background: #eef2ff; color: #3538cd; border-color: #c7d7fe; }
.s-in_progress { background: var(--warn-soft); color: var(--warn); border-color: #fbd9a8; }
.s-resolved { background: var(--success-soft); color: var(--success); border-color: #b9e8cc; }
/* 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 pages: repeatable group cards ---------------------------- */
.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; }

/* ---- My account, Brands, Platform settings ---------------------------
   `.panel` is padding:0 so an evidence table can bleed to its edges, and
   each screen that is NOT such a table adds the gutter back (see #roster
   and the two platform screens above). These three never did, so their
   headings, copy and fields sat flush against the panel border. Same
   gutter as the platform screens, for one rhythm across the admin area. */
#account .panel, #brands .panel, #platform-settings .panel { padding: 18px 20px; }
#account .panel > h2, #brands .panel > h2, #platform-settings .panel > h2 { padding: 0; }
#account .panelhead, #brands .panelhead, #platform-settings .panelhead { margin-bottom: 10px; }
#brands .panelhead { display: flex; align-items: center; justify-content: flex-end; }
#platform-settings .panel > .muted { margin-top: 0; }
/* The notification and brand tables stay full-bleed inside that gutter, so a
   row separator still spans the panel the way the evidence tables do. */
#account .notiftable { width: calc(100% + 40px); margin-left: -20px; }
#account .notiftable th:first-child, #account .notiftable td:first-child { padding-left: 20px; }
#account .notiftable th:last-child, #account .notiftable td:last-child { padding-right: 20px; }

.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; }
}

/* ---- Role workspaces (2026-09) ---------------------------------------- */

/* The no-brand shell: a quiet panel, not an error. The account works; it
  just has nothing granted yet, and the page says exactly that. */
.noaccess {
  max-width: 620px;
  margin: 10vh auto 0;
  padding: 34px 32px 30px;
  text-align: center;
}
.noaccess h2 { margin: 0 0 10px; }
.noaccess .muted { max-width: 46ch; margin: 0 auto 6px; }
.noaccess code { font-size: 13px; }
.noaccess button { margin-top: 14px; }

/* Slim note used for the read-only banners in Settings and the case drawer:
  a notice's weight with one line's height. */
.notice.slimnotice {
  padding: 8px 12px;
  font-size: 13px;
}


/* ---- Dark mode ---------------------------------------------------------
   The interface vocabulary is variable-driven, so dark mode redefines the
   palette once on the root; components keep their semantics. A few legacy
   literals in style.css are patched below where variables never reached. */
html[data-theme="dark"] {
  --bg: #0f1720;
  --panel: #16222c;
  --line: #2a3a47;
  --line-soft: #22303c;
  --text: #dbe7ee;
  --text-2: #b9cad4;
  --muted: #8aa0ae;
  --accent: #4d8dff;
  --accent-soft: #1d3a5f;
  --chip: #1d2b36;
  --chip-line: #31424f;
  --panel-alt: #131f28;
  --row-hover: #182631;
}
html[data-theme="dark"] body { background: var(--bg); color: var(--text); }
html[data-theme="dark"] .panel,
html[data-theme="dark"] .card,
html[data-theme="dark"] .editcard,
html[data-theme="dark"] #detail,
html[data-theme="dark"] .dialogbar,
html[data-theme="dark"] .emptystate,
html[data-theme="dark"] .savedflash,
html[data-theme="dark"] input,
html[data-theme="dark"] select,
html[data-theme="dark"] .tablewrap table { background: var(--panel); color: var(--text); }
html[data-theme="dark"] input,
html[data-theme="dark"] select { border-color: var(--line); }
html[data-theme="dark"] th, html[data-theme="dark"] td { border-color: var(--line); }
html[data-theme="dark"] .notice { background: #1d2b36; border-color: #31424f; color: var(--text-2); }
html[data-theme="dark"] .accountmenu { background: var(--panel); border-color: var(--line); box-shadow: 0 18px 48px #00000066; }

/* ---- Empty states: centred, muted, no shouting ---------------------- */
.emptystate, .empty {
  padding: 32px 20px;
  text-align: center;
  background: var(--panel-alt);
  border: 1px dashed var(--line);
  border-radius: var(--r-lg);
  color: var(--muted);
  font-size: 13.5px;
}
.emptystate small { color: var(--muted); }

/* ---- Sign-in: dark aurora backdrop, light card --------------------- */
#auth {
  position: relative;
  isolation: isolate;
  min-height: 100vh;
  width: 100%;
  margin: 0;
  max-width: none;
  padding: 48px 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--ink);
  overflow: hidden;
}
#auth::before {
  content: "";
  position: absolute;
  inset: -20%;
  z-index: -1;
  background:
    radial-gradient(38% 45% at 18% 18%, rgba(29, 84, 209, .55), transparent 60%),
    radial-gradient(42% 50% at 85% 78%, rgba(94, 234, 212, .28), transparent 60%),
    radial-gradient(60% 60% at 50% 0%, rgba(59, 130, 246, .28), transparent 70%);
  filter: blur(10px);
}
#auth::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: .35;
  background-image:
    linear-gradient(rgba(255, 255, 255, .05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .05) 1px, transparent 1px);
  background-size: 34px 34px;
  -webkit-mask-image: radial-gradient(60% 60% at 50% 30%, #000 10%, transparent 100%);
  mask-image: radial-gradient(60% 60% at 50% 30%, #000 10%, transparent 100%);
}
#auth .mark {
  width: 52px;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--r-lg);
  background: linear-gradient(135deg, var(--accent-2), var(--accent));
  color: #fff;
  font-size: 21px;
  font-weight: 800;
  box-shadow: 0 14px 30px -10px rgba(29, 84, 209, .7);
}
#auth .mark span { color: var(--teal); }
#auth h1 {
  margin: 16px 0 0;
  text-align: center;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -.4px;
  color: #fff;
}
#auth p#authdesc {
  margin: 6px 0 0;
  text-align: center;
  font-size: 13.5px;
  color: #b9c6dd;
}
#auth form#login {
  width: min(360px, 100%);
  margin: 28px 0 0;
  background: var(--panel);
  border: 0;
  border-radius: var(--r-xl);
  padding: 28px 26px 26px;
  box-shadow: var(--shadow-lg);
}
#auth form#login label {
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted);
  margin: 0 0 16px;
}
#auth form#login input {
  display: block;
  width: 100%;
  margin-top: 7px;
  padding: 10px 12px;
  font-size: 14px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--panel);
  color: var(--text);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
#auth form#login input:focus {
  outline: none;
  border-color: var(--accent-2);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
#auth form#login button {
  width: 100%;
  margin-top: 6px;
  padding: 11px 14px;
  font-size: 14px;
}
#auth #error {
  margin: 12px 0 0;
  font-size: 13px;
  color: var(--danger);
  overflow-wrap: break-word;
}
#auth #error:empty { display: none; margin: 0; }
#auth .locale-picker {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin: 24px 0 0;
  padding: 0;
  border-top: 0;
  color: #a9b8d3;
  font-size: 12px;
  font-weight: 600;
}
#auth .locale-picker select {
  background: rgba(255, 255, 255, .08);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, .18);
  border-radius: var(--r-sm);
  padding: 5px 8px;
}
@media (max-width: 480px) {
  #auth { padding: 32px 16px; }
  #auth form#login { padding: 24px 20px 22px; }
}

/* ---- Motion: respected everywhere, not just here -------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
