/* AdOS Design System — enterprise DSP console.
   "One blue, many neutrals", a dark operating rail, and data density over decoration.

   The rail is dark and the canvas is light on purpose. This console is read for hours at a time
   against tables of numbers, and every trading desk UI worth copying (TTD, Beeswax, DV360) puts a
   dark chrome around a light data surface: navigation stops competing with the figures, and the
   eye returns to the same edge every time. Colour in the content area is therefore reserved for
   MEANING — spend, status, a threshold crossed — and never spent on chrome.

   Tokens sourced from AdOS_Design_System_Master_Catalog.csv. */
:root{
  /* brand blue ramp */
  --blue-50:#EAF3FF; --blue-100:#D7E9FF; --blue-200:#B6D7FF; --blue-300:#86BCFF;
  --blue-400:#559FFF; --blue-500:#1769E0; --blue-600:#0F56C7; --blue-700:#0C469F;
  --blue-800:#0B397D; --blue-900:#0B2E63;
  /* neutrals — biased a few degrees toward the brand blue so greys read as chosen, not default */
  --canvas:#F4F6FA; --surface:#FFFFFF; --surface-subtle:#F7F9FC;
  --border:#DCE2EC; --grid:#EBEEF4;
  --text:#0D1526; --muted:#5B6779; --subtle:#8A96A8;
  /* the operating rail — one dark surface, four steps of legibility on it */
  --rail:#0E1626; --rail-2:#151F33; --rail-line:#222E45;
  --rail-text:#E6EBF3; --rail-muted:#93A1B8; --rail-subtle:#6B7A92;
  /* semantic (status only) */
  --success:#0E9F6E; --success-bg:#E8F7F1;
  --warning:#D97706; --warning-bg:#FFF5E5;
  --danger:#D92D20;  --danger-bg:#FEECEB;
  --info:#7C3AED;    --info-bg:#F2EAFF;
  /* data viz */
  --heat-1:#EAF3FF; --heat-2:#B6D7FF; --heat-3:#86BCFF; --heat-4:#1769E0; --heat-5:#6D5CE7;
  --cyan:#0EA5E9;
  /* elevation */
  --shadow-sm:0 1px 2px rgba(16,24,40,.05);
  --shadow-md:0 4px 12px -2px rgba(16,24,40,.08), 0 2px 4px -2px rgba(16,24,40,.06);
  /* geometry — tighter than consumer software; a dense grid reads as instrumentation */
  --r-sm:4px; --r-md:6px; --r-lg:8px; --r-xl:12px;
  --nav-w:236px; --nav-w-collapsed:64px; --topbar-h:52px;
  --row-h:38px;
  /* type */
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --mono:ui-monospace,"SFMono-Regular","Cascadia Code","JetBrains Mono",Menlo,Consolas,monospace;

  /* ---- legacy aliases ----
     Names from the design system this one replaced, still referenced by 21 pages. An undefined
     custom property does not fall back to something sensible — the whole declaration is DROPPED,
     so `color: var(--text-muted)` silently rendered as inherited black, and the pages that wrote
     a fallback carried a DARK value (`var(--line,#2a3242)`) into a light console.
     Aliasing here fixes every one of them without editing 21 files, and keeps the old names
     working for any page written against them later. Do not add new ones. */
  --line:var(--border);
  --card:var(--surface);
  --panel:var(--surface-subtle);
  --surface-2:var(--surface-subtle);
  --bg-subtle:var(--surface-subtle);
  --code-bg:var(--surface-subtle);
  --hover:var(--surface-subtle);
  --text-muted:var(--muted);
  --text-subtle:var(--subtle);
  --blue:var(--blue-500);
}
*{box-sizing:border-box}
html,body{margin:0}
body{background:var(--canvas);color:var(--text);font-family:var(--sans);
  font-size:14px;line-height:20px;-webkit-font-smoothing:antialiased}
a{color:var(--blue-500);text-decoration:none}
a:hover{color:var(--blue-600)}
h1,h2,h3{margin:0;text-wrap:balance}
.tnum{font-variant-numeric:tabular-nums}
.mono{font-family:var(--mono)}

/* ---------- type scale ---------- */
.t-display{font-size:28px;line-height:36px;font-weight:600}
.t-h1{font-size:24px;line-height:32px;font-weight:600}
.t-h2{font-size:20px;line-height:28px;font-weight:600}
.t-h3{font-size:16px;line-height:24px;font-weight:600}
.t-label{font-size:12px;line-height:16px;font-weight:600;letter-spacing:.02em}
.t-caption{font-size:11px;line-height:16px;color:var(--muted)}
.eyebrow{font-size:11px;line-height:16px;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;color:var(--subtle)}

/* ---------- app shell ---------- */
.app{display:grid;grid-template-columns:var(--nav-w) 1fr;min-height:100vh}
.colwrap{min-width:0;display:flex;flex-direction:column}

/* sidebar */
.sidebar{background:var(--rail);border-right:1px solid var(--rail-line);color:var(--rail-text);
  display:flex;flex-direction:column;position:sticky;top:0;height:100vh;overflow-y:auto;
  scrollbar-width:thin;scrollbar-color:var(--rail-line) transparent}
.sidebar::-webkit-scrollbar{width:8px}
.sidebar::-webkit-scrollbar-thumb{background:var(--rail-line);border-radius:100px}
.sidebar .brand{display:flex;align-items:center;gap:10px;padding:15px 16px;
  font-weight:700;font-size:15.5px;letter-spacing:-.01em;color:#fff}
.sidebar .brand .sq{width:26px;height:26px;border-radius:7px;flex:none;
  background:linear-gradient(135deg,var(--blue-500),var(--blue-700));
  display:grid;place-items:center;color:#fff;font-size:13px;font-weight:800}
.sidebar .brand .tag{font-family:var(--mono);font-size:9.5px;color:var(--rail-subtle);
  letter-spacing:.14em;font-weight:500}
.nav{padding:6px 10px;display:flex;flex-direction:column;gap:2px}
.nav .group{font-size:10px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  color:var(--rail-subtle);padding:15px 10px 5px}
.nav a.item{display:flex;align-items:center;gap:10px;padding:7px 10px;border-radius:var(--r-md);
  color:var(--rail-muted);font-weight:500;font-size:13px;border:1px solid transparent;position:relative}
.nav a.item:hover{background:var(--rail-2);color:var(--rail-text)}
/* The active row carries a left marker as well as a fill: at a glance down a 20-item rail, an
   edge is findable in peripheral vision where a background tint alone is not. */
.nav a.item.active{background:var(--rail-2);color:#fff;font-weight:600}
.nav a.item.active:before{content:"";position:absolute;left:-10px;top:6px;bottom:6px;width:3px;
  border-radius:0 3px 3px 0;background:var(--blue-400)}
.nav a.item.active svg{color:var(--blue-300)}
.nav a.item svg{width:17px;height:17px;flex:none;stroke-width:1.9}
.nav a.item .soon{margin-left:auto;font-size:9px;font-family:var(--mono);color:var(--rail-subtle);
  border:1px solid var(--rail-line);border-radius:4px;padding:0 4px}
.sidebar .foot{margin-top:auto;padding:11px 16px;border-top:1px solid var(--rail-line);
  font-size:10.5px;color:var(--rail-subtle);font-family:var(--mono)}

/* topbar */
.topbar{height:var(--topbar-h);background:var(--surface);border-bottom:1px solid var(--border);
  display:flex;align-items:center;gap:16px;padding:0 24px;position:sticky;top:0;z-index:10;
  box-shadow:0 1px 2px rgba(13,21,38,.04)}
.crumb{display:flex;align-items:center;gap:8px;color:var(--muted);font-size:13px}
.crumb .cur{color:var(--text);font-weight:600}
.crumb svg{width:14px;height:14px;color:var(--subtle)}
.topbar .spacer{flex:1}
.topbar .stat{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--muted)}
.topbar .stat b{color:var(--text);font-family:var(--mono);font-variant-numeric:tabular-nums}
.topbar .spend b{color:var(--warning)}
/* Bid latency: two numbers that must read as one measurement, so they share a slot and a
   separator rather than sitting as two independent stats competing with spend. */
.topbar .lat{gap:4px}
.topbar .lat .sep{color:var(--subtle);margin:0 1px}
/* Under ~100 samples a p99 is one request wearing a percentile's clothes. Say so quietly
   instead of showing a confident number. */
.topbar .lat.thin b{color:var(--muted)}
.livedot{width:8px;height:8px;border-radius:50%;background:var(--success);
  box-shadow:0 0 0 0 rgba(14,159,110,.5);animation:pulse 2.2s infinite}
.livedot.off{background:var(--subtle);animation:none}
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(14,159,110,.45)}70%{box-shadow:0 0 0 6px transparent}100%{box-shadow:0 0 0 0 transparent}}

/* content */
/* 1360px was a reading measure applied to instrumentation. A 14-column delivery table on a
   1080p+ monitor should use the monitor; prose blocks set their own measure locally. */
.content{padding:20px 24px 40px;max-width:1680px;width:100%}
.page-head{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;margin-bottom:20px}
.page-head .sub{color:var(--muted);font-size:13.5px;margin-top:4px}

/* ---------- cards ---------- */
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);
  box-shadow:var(--shadow-sm)}
.card-pad{padding:18px 20px}
.card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:14px 20px;border-bottom:1px solid var(--grid)}
.card-head h3{font-size:15px;font-weight:600}
.card-head .hint{font-size:12px;color:var(--subtle)}
.grid{display:grid;gap:16px}
.cols-4{grid-template-columns:repeat(4,1fr)}
.cols-3{grid-template-columns:repeat(3,1fr)}
.cols-2{grid-template-columns:repeat(2,1fr)}
@media(max-width:1100px){.cols-4{grid-template-columns:repeat(2,1fr)}.cols-3{grid-template-columns:repeat(2,1fr)}}
@media(max-width:720px){.cols-4,.cols-3,.cols-2{grid-template-columns:1fr}}

/* ---------- KPI tiles ---------- */
.kpi{background:var(--surface);border:1px solid var(--border);border-radius:var(--r-lg);
  padding:16px 18px;box-shadow:var(--shadow-sm)}
.kpi .label{font-size:12px;font-weight:600;color:var(--muted);display:flex;align-items:center;gap:6px}
.kpi .value{font-size:26px;line-height:32px;font-weight:600;margin-top:8px;
  font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.kpi .value.spend{color:var(--warning)}
.kpi .value.blue{color:var(--blue-600)}
.kpi .foot{font-size:12px;color:var(--subtle);margin-top:6px}
.kpi .foot b{color:var(--muted);font-variant-numeric:tabular-nums}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;gap:7px;font-family:var(--sans);font-size:13.5px;
  font-weight:600;padding:8px 14px;border-radius:var(--r-md);border:1px solid var(--border);
  background:var(--surface);color:var(--text);cursor:pointer;transition:.12s}
.btn:hover{background:var(--surface-subtle)}
.btn svg{width:16px;height:16px}
.btn-primary{background:var(--blue-500);border-color:var(--blue-500);color:#fff}
.btn-primary:hover{background:var(--blue-600);border-color:var(--blue-600)}
.btn-danger{background:var(--surface);border-color:var(--danger);color:var(--danger)}
.btn-danger:hover{background:var(--danger-bg)}
.btn-ghost{border-color:transparent;background:transparent;color:var(--muted)}
.btn-ghost:hover{background:var(--surface-subtle);color:var(--text)}
.btn:disabled{opacity:.55;cursor:not-allowed}
/* Half the console writes `btn primary` and `btn sm`, which until now styled as
   nothing — so primary buttons rendered grey and in-table buttons rendered full
   size. Make the markup that already exists mean what it says. */
.btn.sm{font-size:12.5px;padding:5px 10px;gap:5px}
.btn.primary{background:var(--blue-500);border-color:var(--blue-500);color:#fff}
.btn.primary:hover{background:var(--blue-600);border-color:var(--blue-600)}

/* ---------- pills / chips ---------- */
.pill{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:600;
  padding:2px 9px;border-radius:100px;border:1px solid var(--border);color:var(--muted);background:var(--surface-subtle)}
.pill-success{color:var(--success);background:var(--success-bg);border-color:transparent}
.pill-warning{color:var(--warning);background:var(--warning-bg);border-color:transparent}
.pill-danger{color:var(--danger);background:var(--danger-bg);border-color:transparent}
.pill-blue{color:var(--blue-700);background:var(--blue-50);border-color:transparent}
.pill-dot{width:6px;height:6px;border-radius:50%;background:currentColor}
.chip{font-family:var(--mono);font-size:12px;padding:4px 8px;border-radius:var(--r-sm);
  border:1px solid var(--border);background:var(--surface-subtle);color:var(--blue-700);cursor:pointer}
.chip:hover{background:var(--blue-50);border-color:var(--blue-300)}

/* ---------- tables ---------- */
/* Density is the point. These tables are scanned for outliers across many rows at once, so the
   row height is set to fit more of them on screen rather than to feel airy — the same reason a
   trading blotter is dense. Numerals are tabular everywhere so columns of figures align on the
   decimal and a longer number reads as a bigger number. */
.tbl{width:100%;border-collapse:collapse;font-size:12.5px}
.tbl th{text-align:left;font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  color:var(--subtle);padding:8px 12px;border-bottom:1px solid var(--border);white-space:nowrap;
  background:var(--surface-subtle)}
.tbl td{padding:7px 12px;border-bottom:1px solid var(--grid);font-variant-numeric:tabular-nums;
  vertical-align:middle;height:var(--row-h)}
/* Comfortable mode, for the few tables that are read rather than scanned. */
.tbl.roomy td{padding:11px 14px}
.tbl tr:last-child td{border-bottom:0}
.tbl tbody tr:hover{background:var(--blue-50)}
/* The identifier column anchors the scan, so it stays put on a wide table. */
.tbl td.stick,.tbl th.stick{position:sticky;left:0;background:var(--surface);z-index:1}
.tbl th.stick{background:var(--surface-subtle);z-index:3}
.tbl tbody tr:hover td.stick{background:var(--blue-50)}
.tbl .num{text-align:right}
.tbl-wrap{overflow-x:auto;max-width:100%}

/* ---------- forms ---------- */
.field{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}
.field label{font-size:12.5px;font-weight:600;color:var(--text)}
.field .help{font-size:11.5px;color:var(--subtle)}
.input,.select,.textarea{font-family:var(--sans);font-size:13.5px;color:var(--text);
  background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);padding:9px 12px;width:100%}
.input.mono,.textarea.mono{font-family:var(--mono);font-size:12.5px}
.input:focus,.select:focus,.textarea:focus{outline:none;border-color:var(--blue-400);
  box-shadow:0 0 0 3px var(--blue-50)}
.textarea{resize:vertical;min-height:120px;line-height:1.5}
/* Some pages put class="field" on the wrapper, others on the control itself.
   Style both, rather than leaving a third of the console's inputs unstyled. */
input.field,select.field,textarea.field{display:block;margin-bottom:0;font-family:var(--sans);
  font-size:13.5px;color:var(--text);background:var(--surface);border:1px solid var(--border);
  border-radius:var(--r-md);padding:9px 12px;width:100%}
input.field:focus,select.field:focus,textarea.field:focus{outline:none;border-color:var(--blue-400);
  box-shadow:0 0 0 3px var(--blue-50)}
textarea.field{resize:vertical;line-height:1.5}
.field-wrap{display:flex;flex-direction:column;gap:6px}
.row{display:flex;gap:12px}.row>*{flex:1}

/* toggle switch */
.switch{position:relative;display:inline-block;width:40px;height:22px;flex:none}
.switch input{opacity:0;width:0;height:0}
.switch .slider{position:absolute;inset:0;background:var(--border);border-radius:100px;transition:.16s;cursor:pointer}
.switch .slider:before{content:"";position:absolute;height:16px;width:16px;left:3px;top:3px;
  background:#fff;border-radius:50%;transition:.16s;box-shadow:var(--shadow-sm)}
.switch input:checked+.slider{background:var(--success)}
.switch input:checked+.slider:before{transform:translateX(18px)}
.switch.danger input:checked+.slider{background:var(--success)}
.switch.danger input:not(:checked)+.slider{background:var(--danger)}

/* ---------- funnel ---------- */
.funnel{display:flex;flex-direction:column;gap:8px}
.fstage{position:relative;border:1px solid var(--border);border-radius:var(--r-md);
  padding:11px 14px;display:grid;grid-template-columns:24px 1fr auto auto;align-items:center;gap:12px;overflow:hidden;background:var(--surface)}
.fstage .bar{position:absolute;inset:0 auto 0 0;background:var(--blue-50);z-index:0}
.fstage.client .bar{background:#E7F7FA}
.fstage>*{position:relative;z-index:1}
.fstage .n{font-family:var(--mono);color:var(--subtle);font-size:12px}
.fstage .lbl{font-weight:600;font-size:13.5px}
.fstage.server .lbl{color:var(--blue-700)}
.fstage.client .lbl{color:var(--cyan)}
.fstage .ct{font-family:var(--mono);font-variant-numeric:tabular-nums;font-weight:600}
.fstage .cv{font-family:var(--mono);font-size:11.5px;color:var(--muted);min-width:52px;text-align:right}

/* ---------- toast ---------- */
.toast-wrap{position:fixed;bottom:22px;right:22px;display:flex;flex-direction:column;gap:8px;z-index:100}
.toast{background:var(--text);color:#fff;padding:11px 16px;border-radius:var(--r-md);font-size:13px;
  box-shadow:var(--shadow-md);animation:toastin .2s}
.toast.ok{background:var(--success)}.toast.err{background:var(--danger)}
@keyframes toastin{from{opacity:0;transform:translateY(6px)}}

/* skeleton + empty */
.skel{background:linear-gradient(90deg,var(--grid),var(--surface-subtle),var(--grid));
  background-size:200% 100%;animation:sk 1.2s infinite;border-radius:var(--r-sm)}
@keyframes sk{to{background-position:-200% 0}}
.empty{text-align:center;color:var(--muted);padding:48px 20px}
.empty h3{color:var(--text);margin-bottom:6px}

/* ---------- affiliate console components ----------
   Everything below was previously pasted into individual pages, each with its own measurements.
   The drawer existed at three widths, the status pills in six colour sets that shadowed the
   semantic .pill-* above, and the period rail as both a button group and a <select>. Declared once
   here so a component means one thing across the console and the partner portal. Paired with
   ados-aff.js, which is what renders most of it. */

/* workspace switcher — AdOS is two products in one console (media buying, affiliate network).
   This is how you tell which one you are in, and how you get to the other. Before it, the whole
   affiliate network was five rows buried in a 27-item rail with no label saying so. */
.wsw{position:relative;padding:0 10px 8px;margin-top:-4px}
.wsw-btn{display:flex;align-items:center;gap:10px;width:100%;padding:9px 10px;cursor:pointer;
  border:1px solid var(--rail-line);border-radius:var(--r-md);background:var(--rail-2);
  font-family:var(--sans);text-align:left;color:var(--rail-text)}
.wsw-btn:hover{border-color:var(--blue-600);background:#1B2740}
.wsw-btn svg{width:16px;height:16px;flex:none;color:var(--rail-subtle);stroke-width:2}
.wsw-btn .lbl{flex:1;min-width:0}
.wsw-btn .lbl b{display:block;font-size:13px;font-weight:600;line-height:16px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wsw-btn .lbl i{display:block;font-style:normal;font-size:10.5px;color:var(--rail-subtle);line-height:14px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wsw-btn .caret{color:var(--rail-subtle);font-size:11px}
.wsw-menu{display:none;position:absolute;left:10px;right:10px;top:100%;z-index:30;
  background:var(--surface);border:1px solid var(--border);border-radius:var(--r-md);
  box-shadow:var(--shadow-md);overflow:hidden}
.wsw.open .wsw-menu{display:block}
.wsw-menu a{display:block;padding:10px 12px;color:var(--text)}
.wsw-menu a:hover{background:var(--surface-subtle)}
.wsw-menu a.on{background:var(--blue-50)}
.wsw-menu a b{display:block;font-size:13px;font-weight:600}
.wsw-menu a i{display:block;font-style:normal;font-size:10.5px;color:var(--subtle)}

/* period rail — six named windows, one click each */
.periods{display:flex;gap:6px;flex-wrap:wrap}
.periods button{font:600 13px var(--sans);padding:7px 13px;border-radius:var(--r-md);
  border:1px solid var(--border);background:var(--surface);color:var(--muted);cursor:pointer;transition:.12s}
.periods button:hover{border-color:var(--blue-300);color:var(--blue-700)}
.periods button.on{background:var(--blue-600);border-color:var(--blue-600);color:#fff}

/* custom range, sitting beside the rail rather than replacing it */
.daterange{display:flex;align-items:center;gap:8px}
.daterange .input{width:auto;padding:6px 10px;font-size:12.5px}

/* filter bar — the row of controls above a table */
.filters{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:12px 20px;border-bottom:1px solid var(--grid)}
.filters .input,.filters .select{width:auto;min-width:150px;padding:7px 11px;font-size:13px}
.filters .grow{flex:1}
.filters .spacer{flex:1}

/* drawer + scrim — ONE width, owning its own header and close button */
.drawer{position:fixed;top:0;right:0;width:620px;max-width:96vw;height:100%;
  background:var(--surface);border-left:1px solid var(--border);
  box-shadow:-8px 0 24px rgba(16,24,40,.08);padding:20px 24px 40px;overflow:auto;z-index:50;display:none}
.drawer.open{display:block}
.drawer-head{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding-bottom:14px;margin-bottom:16px;border-bottom:1px solid var(--grid);
  position:sticky;top:-20px;background:var(--surface);padding-top:20px;margin-top:-20px;z-index:1}
.scrim{position:fixed;inset:0;background:rgba(16,24,40,.35);z-index:40;display:none}
.scrim.open{display:block}
@media(max-width:720px){.drawer{width:100vw}}

/* tabs — for detail pages that were one six-screen scroll with no way to jump */
.tabs{display:flex;gap:2px;border-bottom:1px solid var(--border);margin-bottom:18px;overflow-x:auto}
.tabs button{font:600 13.5px var(--sans);padding:10px 14px;border:0;border-bottom:2px solid transparent;
  background:transparent;color:var(--muted);cursor:pointer;white-space:nowrap}
.tabs button:hover{color:var(--text)}
.tabs button.on{color:var(--blue-700);border-bottom-color:var(--blue-500)}
.tabpane{display:none}
.tabpane.on{display:block}

/* sortable headers */
.tbl th.sortable{cursor:pointer;user-select:none}
.tbl th.sortable:hover{color:var(--muted)}
.tbl th.sortable:after{content:"↕";margin-left:5px;opacity:.35;font-size:10px}
.tbl th.sortable[data-dir=asc]:after{content:"↑";opacity:1;color:var(--blue-600)}
.tbl th.sortable[data-dir=desc]:after{content:"↓";opacity:1;color:var(--blue-600)}
/* a header row that survives a long table */
.tbl.sticky thead th{position:sticky;top:var(--topbar-h);background:var(--surface);z-index:2}
/* ...but ONLY when the page is what scrolls. Inside .tbl-wrap the sticky context is the wrap, not
   the document, so the topbar offset pushes the header down INSIDE the box and leaves a blank band
   above it — which is exactly what it did on /bid-controls. There, stick to the top of the wrap. */
.tbl-wrap .tbl.sticky thead th{top:0}
/* selection column */
.tbl td.pick,.tbl th.pick{width:36px;padding-right:0}
.tbl tr.picked td{background:var(--blue-50)}
/* the totals row: same numbers, visibly not one of the rows */
.tbl tfoot td{font-weight:600;border-top:2px solid var(--border);background:var(--surface-subtle)}

/* pagination */
.pager{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 20px;
  border-top:1px solid var(--grid)}
.pager-btns{display:flex;gap:8px}

/* bulk action bar, shown only when rows are selected */
.bulkbar{display:none;align-items:center;gap:12px;padding:10px 20px;
  background:var(--blue-50);border-bottom:1px solid var(--blue-100)}
.bulkbar.on{display:flex}
.bulkbar .count{font-weight:600;color:var(--blue-700)}

/* monospace value box — tracking links, postback URLs, keys */
.linkbox{font-family:var(--mono);font-size:11.5px;background:var(--surface-subtle);
  border:1px solid var(--grid);border-radius:var(--r-sm);padding:6px 8px;
  overflow-x:auto;white-space:nowrap;max-width:100%}

/* cap usage — how much of an offer's ceiling is spent */
.capbar{height:6px;border-radius:100px;background:var(--grid);overflow:hidden;min-width:70px}
.capbar i{display:block;height:100%;background:var(--blue-500)}
.capbar.warn i{background:var(--warning)}
.capbar.full i{background:var(--danger)}

/* clickable rows, for tables that drill somewhere */
.tbl tr.clickable{cursor:pointer}

/* freshness note — goes amber once the rollup is behind */
.t-caption.stale{color:var(--warning);font-weight:600}

/* definition grid, for the identity card on a detail page */
.deflist{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:14px 20px}
.deflist .t-label{color:var(--subtle)}
.deflist .v{font-size:14px;font-weight:600;margin-top:2px}

/* responsive rail collapse */
@media(max-width:1280px){
  .app{grid-template-columns:var(--nav-w-collapsed) 1fr}
  .sidebar .brand span.txt,.sidebar .brand .tag,.nav .group,.nav a.item .lbl,.nav a.item .soon,.sidebar .foot{display:none}
  .nav a.item{justify-content:center}
  /* The switcher collapses to its icon but must stay reachable — at this width the rail is
     nothing but glyphs, so which product you are looking at is otherwise unknowable. */
  .wsw-btn .lbl,.wsw-btn .caret{display:none}
  .wsw-btn{justify-content:center;padding:9px 0}
  .wsw-menu{left:6px;width:210px;right:auto}
}
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}


/* ================================================================================================
   Trading-desk components
   Added with the console rebuild. Each one existed as a bespoke, differently-measured copy inside
   individual pages, or did not exist and was faked with a number in a cell.
   ================================================================================================ */

/* ---------- toolbar: the control strip that owns a table ----------
   Distinct from .filters (which sits INSIDE a card, above its rows). The toolbar sits ABOVE the
   card and carries what acts on the whole view: the period, the search, saved views, export. */
.toolbar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:14px}
.toolbar .spacer{flex:1}
.toolbar .input,.toolbar .select{width:auto;min-width:150px;padding:6px 10px;font-size:12.5px}

/* segmented control — mutually exclusive views, shown as one object rather than N buttons, so it
   reads as "pick one" instead of "press any" */
.seg{display:inline-flex;border:1px solid var(--border);border-radius:var(--r-md);overflow:hidden;
  background:var(--surface)}
.seg button{font:600 12.5px var(--sans);padding:6px 12px;border:0;border-right:1px solid var(--border);
  background:transparent;color:var(--muted);cursor:pointer;white-space:nowrap}
.seg button:last-child{border-right:0}
.seg button:hover{background:var(--surface-subtle);color:var(--text)}
.seg button.on{background:var(--blue-500);color:#fff}
.seg button.on:hover{background:var(--blue-600)}

/* search field with its glyph inside, for the one control every table needs */
.search{position:relative;display:inline-flex;align-items:center}
.search svg{position:absolute;left:9px;width:14px;height:14px;color:var(--subtle);pointer-events:none}
.search input{padding-left:29px}

/* ---------- KPI deltas ----------
   A number with no comparison cannot be acted on: $94 of spend is either fine or an emergency
   depending on yesterday. Direction is carried by an arrow AND a colour, never colour alone. */
.kpi .delta{display:inline-flex;align-items:center;gap:3px;font-size:11.5px;font-weight:600;
  font-variant-numeric:tabular-nums;margin-top:6px}
.kpi .delta.up{color:var(--success)}
.kpi .delta.down{color:var(--danger)}
.kpi .delta.flat{color:var(--subtle)}
.kpi .delta:before{font-size:10px}
.kpi .delta.up:before{content:"\2191"}
.kpi .delta.down:before{content:"\2193"}
.kpi .delta.flat:before{content:"\2192"}
/* For metrics where down is the good direction — CPA, frequency, blocked rate. Costs one class at
   the call site and prevents the chart lying about whether a change was welcome. */
.kpi .delta.inverse.up{color:var(--danger)}
.kpi .delta.inverse.down{color:var(--success)}
.kpi .spark{margin-top:10px;display:block;width:100%;height:28px;overflow:visible}
.kpi .spark path.line{fill:none;stroke:var(--blue-500);stroke-width:1.6;
  stroke-linejoin:round;stroke-linecap:round;vector-effect:non-scaling-stroke}
.kpi .spark path.area{fill:var(--blue-50);stroke:none}
.kpi .spark circle{fill:var(--blue-600)}

/* ---------- in-cell magnitude bar ----------
   A column of right-aligned numbers hides its own shape. A bar behind the figure makes the
   distribution visible without a chart, which is what turns "35% of spend" from a number you
   have to compute into one you can see. */
.bar-cell{position:relative;display:flex;align-items:center;justify-content:flex-end;gap:8px}
.bar-cell .track{position:absolute;inset:auto 0 -2px 0;height:3px;background:var(--grid);border-radius:2px}
.bar-cell .track i{display:block;height:100%;background:var(--blue-300);border-radius:2px}
.bar-cell.warn .track i{background:var(--warning)}
.bar-cell.bad .track i{background:var(--danger)}

/* ---------- column picker ----------
   A 20-column report is unusable for any single question. Rather than shipping five near-identical
   tables, ship one and let the operator choose the columns. */
.colpick{position:relative}
.colpick-menu{display:none;position:absolute;right:0;top:calc(100% + 6px);z-index:30;min-width:220px;
  max-height:340px;overflow:auto;background:var(--surface);border:1px solid var(--border);
  border-radius:var(--r-md);box-shadow:var(--shadow-md);padding:6px}
.colpick.open .colpick-menu{display:block}
.colpick-menu label{display:flex;align-items:center;gap:9px;padding:6px 8px;border-radius:var(--r-sm);
  font-size:12.5px;cursor:pointer}
.colpick-menu label:hover{background:var(--surface-subtle)}
.colpick-menu .sep{height:1px;background:var(--grid);margin:5px 2px}

/* ---------- status dot, for a state that must read at a glance in a dense row ---------- */
.dot{display:inline-block;width:7px;height:7px;border-radius:50%;flex:none;background:var(--subtle)}
.dot.live{background:var(--success)}
.dot.paused{background:var(--warning)}
.dot.off{background:var(--subtle)}
.dot.error{background:var(--danger)}

/* ---------- inline metric, for a figure that belongs beside a label rather than in a tile ---- */
.metric{display:flex;flex-direction:column;gap:2px}
.metric .k{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--subtle)}
.metric .v{font-size:17px;font-weight:600;font-variant-numeric:tabular-nums;letter-spacing:-.01em}

/* ---------- evidence note ----------
   Every automated action in this console (auto-deny, a bid rule, a quarantine) has a reason, and
   the reason is the difference between a control an operator trusts and one they switch off. */
.evidence{border-left:2px solid var(--blue-200);background:var(--blue-50);padding:9px 12px;
  border-radius:0 var(--r-sm) var(--r-sm) 0;font-size:12px;color:var(--muted);max-width:80ch}
.evidence b{color:var(--text)}
.evidence.warn{border-left-color:var(--warning);background:var(--warning-bg)}
.evidence.bad{border-left-color:var(--danger);background:var(--danger-bg)}

/* keyboard focus must be visible on every interactive thing, including the dark rail */
:where(a,button,input,select,textarea,[tabindex]):focus-visible{outline:2px solid var(--blue-400);
  outline-offset:2px;border-radius:var(--r-sm)}
.sidebar :where(a,button):focus-visible{outline-color:var(--blue-300)}


/* ---------- global period control (topbar) ---------- */
.tb-range{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--muted)}
.tb-range select{font:600 12.5px var(--sans);color:var(--text);background:var(--surface);
  border:1px solid var(--border);border-radius:var(--r-md);padding:5px 8px}
.tb-range select:focus{outline:none;border-color:var(--blue-400);box-shadow:0 0 0 3px var(--blue-50)}
@media(max-width:900px){.tb-range span{display:none}}

/* ---------- table engine furniture (ados-table.js) ----------
   The bar sits ABOVE the card that holds the table, so filtering and column choice read as acting
   on the whole view rather than on one column of it. */
.tbl-bar{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 10px}
.tbl-bar .spacer{flex:1}
.tbl-bar .input{width:220px;padding:6px 10px 6px 29px;font-size:12.5px}
.tbl-count{white-space:nowrap;font-variant-numeric:tabular-nums}
.tbl th.sortable{cursor:pointer;user-select:none}
.tbl th.sortable:hover{color:var(--text)}
/* The indicator lives in ::after; a sorted column also gets a tint so the sort is findable without
   reading every header. */
.tbl th[data-dir]{color:var(--blue-700);background:var(--blue-50)}
.pager{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:10px 2px 0;border-top:0}
.pager-info{font-variant-numeric:tabular-nums}


/* ================================================================================================
   IA rebuild primitives (ados-ui.js)
   ================================================================================================ */

/* terminology — a dotted underline is the only affordance that says "there is an explanation here"
   without spending layout on it */
.has-def{border-bottom:1px dotted var(--subtle);cursor:help}

/* tabs used as the consolidation device: two lenses on one subject, no navigation between them */
.wtabs{display:flex;gap:2px;border-bottom:1px solid var(--border);margin:0 0 18px;overflow-x:auto}
.wtabs button{font:600 13.5px var(--sans);padding:9px 14px;border:0;border-bottom:2px solid transparent;
  background:transparent;color:var(--muted);cursor:pointer;white-space:nowrap}
.wtabs button:hover{color:var(--text)}
.wtabs button.on{color:var(--blue-700);border-bottom-color:var(--blue-500)}
[data-pane]{display:none}
[data-pane].on{display:block}

/* command palette */
.palette{position:fixed;inset:0;background:rgba(13,21,38,.45);z-index:200;display:none;
  align-items:flex-start;justify-content:center;padding-top:12vh}
.palette.open{display:flex}
.palette-box{width:640px;max-width:92vw;background:var(--surface);border:1px solid var(--border);
  border-radius:var(--r-lg);box-shadow:var(--shadow-md);overflow:hidden}
.palette-box .input{border:0;border-bottom:1px solid var(--grid);border-radius:0;padding:14px 16px;font-size:15px}
.palette-box .input:focus{box-shadow:none;border-color:var(--grid)}
.palette-list{max-height:52vh;overflow:auto;padding:6px}
.palette-row{display:flex;align-items:baseline;gap:10px;padding:9px 12px;border-radius:var(--r-md);color:var(--text)}
.palette-row:hover{background:var(--blue-50)}
.palette-row b{font-weight:600;font-size:13.5px}
.palette-row i{font-style:normal;font-size:11.5px;color:var(--subtle);font-family:var(--mono)}

/* attention list — the Command Center's only editorial element: things a human must decide */
.attn{display:flex;flex-direction:column;gap:8px}
.attn-row{display:flex;align-items:flex-start;gap:11px;padding:11px 13px;border:1px solid var(--border);
  border-left:3px solid var(--warning);border-radius:var(--r-md);background:var(--surface)}
.attn-row.bad{border-left-color:var(--danger)}
.attn-row.ok{border-left-color:var(--success)}
.attn-row .what{font-weight:600;font-size:13px}
.attn-row .why{font-size:12px;color:var(--muted);margin-top:2px;max-width:88ch}
.attn-row .act{margin-left:auto;white-space:nowrap}

/* optimizer decision feed — every automatic action states its reason inline, never just its verb */
.feed{display:flex;flex-direction:column;gap:0}
.feed-row{display:grid;grid-template-columns:132px 1fr auto;gap:12px;padding:9px 0;
  border-bottom:1px solid var(--grid);align-items:baseline}
.feed-row:last-child{border-bottom:0}
.feed-row .verb{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.04em}
.feed-row .why{font-size:12.5px;color:var(--muted)}
.feed-row .when{font-size:11.5px;color:var(--subtle);font-variant-numeric:tabular-nums}

/* health strip */
.health{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px}
.health .h{border:1px solid var(--border);border-radius:var(--r-md);padding:11px 13px;background:var(--surface)}
.health .h .k{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--subtle)}
.health .h .v{font-size:16px;font-weight:600;font-variant-numeric:tabular-nums;margin-top:3px}
.health .h .n{font-size:11.5px;color:var(--subtle);margin-top:2px}

/* funnel snapshot — horizontal stages, each showing its own carry-through */
.fsnap{display:flex;gap:6px;overflow-x:auto;padding-bottom:4px}
.fsnap .st{flex:1;min-width:104px;border:1px solid var(--border);border-radius:var(--r-md);
  padding:9px 11px;background:var(--surface);position:relative}
.fsnap .st .k{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--subtle);white-space:nowrap}
.fsnap .st .v{font-size:17px;font-weight:600;font-variant-numeric:tabular-nums;margin-top:3px}
.fsnap .st .r{font-size:11px;color:var(--muted);margin-top:1px;font-variant-numeric:tabular-nums}
.fsnap .st.drop{border-color:var(--danger);background:var(--danger-bg)}
