* { margin: 0; padding: 0; box-sizing: border-box; }

/* ═══════════════════════════════════════════════════════════════════════════
   ASE — "SIGNAL"  (full rebrand, 2026-10-18)

   A rebrand, not a reskin. Everything that made the old build read as a stock
   admin panel is gone:
     · the left sidebar        -> a two-tier horizontal masthead, content full-bleed
     · gold / indigo / Inter   -> near-black, warm cream ink, ONE electric lime
     · pillowy cards + shadows -> hairline rules and flat planes
     · generic sans            -> Archivo (editorial grotesque) + JetBrains Mono

   RULES (explicit so this cannot drift back to defaults):
   1. ONE signal colour: lime #c3f53c. It marks the live, the active and the
      selected. Never decoration. Semantic red/amber/green are the only others.
   2. INK IS CREAM (#ece7dc), never pure white — warm ink on cold black is the
      whole look.
   3. DEPTH IS LUMINANCE: #070708 -> #101012 -> #18181b -> #222226. No shadows;
      they do not exist on black.
   4. SPACING SCALE: 4 8 12 16 24 32 48. Between-group space > within-group.
   5. RADII ARE NEARLY SQUARE (4-6px). Roundness reads consumer; this is an
      instrument.
   6. EVERY number, id, URL and suffix is MONO and tabular.

   OLD variable names are preserved verbatim as this sheet's public API so every
   existing component keeps working.
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
  color-scheme: dark;

  /* ─── DARK (default) — Radix slate-dark + amber-dark + jade/red-dark ───
     Radix step semantics: 1 app bg · 2 card · 3-5 component states ·
     6-8 borders · 9 solid fill · 11-12 text (guaranteed Lc60/Lc90 APCA).
     Every pair below was measured, not eyeballed. */
  --bg-primary: #111113;   /* slate-1  */
  --bg-secondary: #18191b; /* slate-2  */
  --bg-card: #18191b;      /* slate-2  */
  --bg-input: #111113;     /* slate-1  */
  --raise: #272a2d;        /* slate-4  */
  --overlay: #2e3135;      /* slate-5  */
  --border: #363a3f;       /* slate-6  */
  --border-2: #212225;     /* slate-3  */
  --border-hi: #5a6169;    /* slate-8  */

  --text-primary: #edeef0;   /* slate-12 · 15.2:1 on card */
  --text-secondary: #b0b4ba; /* slate-11 ·  8.5:1 */
  --text-muted: #777b84;     /* slate-10 ·  4.2:1 */

  --accent: #ffc53d;         /* amber-9  · 11.2:1 */
  --accent-hover: #ffd60a;   /* amber-10 */
  --accent-ink: #16120c;     /* amber-1  · 11.8:1 on accent */
  --accent-soft: #302008;    /* amber-3  */
  --accent-line: #714f19;    /* amber-7  */
  --primary: #ffc53d;
  --primary-hover: #ffd60a;

  --success: #1fd8a4;  --ok-bg: #0f2e22;   --ok-ln: #246854;   /* jade  */
  --warning: #ffca16;  --warn-bg: #302008; --warn-ln: #714f19; /* amber */
  --error:   #ff9592;  --bad-bg: #3b1219;  --bad-ln: #8c333a;  /* red   */
  --danger:  #ff9592;
  --critical:#e5484d;
  --info:    #70b8ff;

  --scroll-thumb: #43484e;
  --scroll-thumb-hi: #5a6169;

  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s6: 24px; --s8: 32px; --s12: 48px;
  --r: 6px;
  --r-sm: 4px;
  --sidebar-width: 0px;
  --nav-h: 56px;
  --ctx-h: 46px;
  --glass: rgba(255,255,255,0.035);
  --font-ui: 'Archivo', ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Monaco, monospace;
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
}

/* ─── LIGHT — Radix slate + amber + jade/red (light) ───
   Same hues as dark, so switching theme never changes the product's identity.
   NOTE the accent: amber-9 (#ffc53d) is Radix's SOLID-FILL step and measures
   1.58:1 as text on white — illegible. Light therefore uses amber-11 (#ab6400,
   4.6:1), which is the text-safe step of the same hue. */
:root[data-theme="light"] {
  color-scheme: light;

  --bg-primary: #f9f9fb;   /* slate-2  */
  --bg-secondary: #f0f0f3; /* slate-3  */
  --bg-card: #ffffff;
  --bg-input: #ffffff;
  --raise: #f0f0f3;        /* slate-3  */
  --overlay: #e8e8ec;      /* slate-4  */
  --border: #d9d9e0;       /* slate-6  */
  --border-2: #e8e8ec;     /* slate-4  */
  --border-hi: #b9bbc6;    /* slate-8  */

  --text-primary: #1c2024;   /* slate-12 · 16.4:1 on white */
  --text-secondary: #60646c; /* slate-11 ·  5.9:1 */
  --text-muted: #80838d;     /* slate-10 ·  3.8:1 */

  --accent: #ab6400;         /* amber-11 · 4.6:1 */
  --accent-hover: #8f5400;
  --accent-ink: #ffffff;
  --accent-soft: #fff7c2;    /* amber-3  */
  --accent-line: #e9c162;    /* amber-7  */
  --primary: #ab6400;
  --primary-hover: #8f5400;

  --success: #208368;  --ok-bg: #e6f7ed;   --ok-ln: #8bceb6;
  --warning: #ab6400;  --warn-bg: #fff7c2; --warn-ln: #e9c162;
  --error:   #ce2c31;  --bad-bg: #feebec;  --bad-ln: #f4a9aa;
  --danger:  #ce2c31;
  --critical:#dc3e42;
  --info:    #0d74ce;

  --scroll-thumb: #cdced6;
  --scroll-thumb-hi: #b9bbc6;
  --glass: rgba(0,0,0,0.035);
}


body {
  font-family: var(--font-ui);
  background: var(--bg-primary);
  color: var(--text-primary);
  min-height: 100vh;
  font-size: 13.5px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  -webkit-text-size-adjust: 100%;
}

/* Mono is reserved for machine-readable strings (IDs, URLs, suffixes, codes).
   Table body text — campaign names above all — is SANS: a grid of monospaced
   prose reads like a terminal dump, not a product. Numerals stay tabular. */
.hop-url, .chain-url, .report-value, .param-tag, .status-badge,
.version, code, kbd, pre, input, textarea, select {
  font-family: var(--font-mono);
}
td, .value, th { font-variant-numeric: tabular-nums; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
::selection { background: var(--accent); color: var(--accent-ink); }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg-primary); }
::-webkit-scrollbar-thumb { background: var(--scroll-thumb); border: 2px solid var(--bg-primary); border-radius: 5px; }
::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hi); }

/* ═══════════ SHELL: stacked, no sidebar ═══════════ */
.layout { display: flex; flex-direction: column; min-height: 100vh; }

/* ── TIER 1: brand masthead (this IS the old .sidebar element, turned sideways) ── */
.sidebar {
  position: sticky; top: 0; z-index: 100;
  width: 100%;
  height: var(--nav-h);
  background: var(--bg-secondary);
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--s6);
  padding: 0 var(--s6);
  transition: transform .25s cubic-bezier(.4,0,.2,1);
}

.sidebar-header {
  display: flex; align-items: center; gap: 11px;
  border: 0; padding: 0;
  flex: none;
}

/* The mark: a lime signal block. Square, not rounded — an indicator light. */
.logo-icon-sm {
  width: 26px; height: 26px;
  background: var(--accent);
  border: 0; border-radius: 3px;
  display: flex; align-items: center; justify-content: center;
  color: var(--accent-ink);
  flex-shrink: 0;
}
.logo-icon-sm svg { width: 15px; height: 15px; }

.sidebar-header h2 {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-primary);
  line-height: 1;
}

.version {
  display: inline-block;
  font-size: 9px;
  color: var(--accent);
  background: transparent;
  border: 1px solid var(--accent-line);
  padding: 1px 5px;
  border-radius: 2px;
  font-weight: 500;
  letter-spacing: .1em;
  text-transform: uppercase;
  margin-left: 2px;
  vertical-align: middle;
}

/* horizontal nav */
.nav-links {
  list-style: none;
  display: flex; align-items: center; gap: 2px;
  flex: 1; min-width: 0;
  padding: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.nav-links::-webkit-scrollbar { display: none; }

.nav-link {
  display: flex; align-items: center; gap: 7px;
  height: 30px; padding: 0 11px;
  border-radius: var(--r-sm);
  color: var(--text-secondary);
  text-decoration: none;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .02em;
  position: relative;
  transition: color .14s, background .14s;
}
.nav-link:hover { color: var(--text-primary); background: var(--glass); }
.nav-link.active { color: var(--accent-ink); background: var(--accent); font-weight: 600; }

/* In the horizontal masthead the icons are suppressed: ten destinations plus
   glyphs overflow 1440px and silently clipped "Settings" off the end. Labels
   alone fit, and read more editorial. The icons return in the mobile drawer. */
.nav-icon { display: none; width: 15px; height: 15px; fill: currentColor; flex-shrink: 0; opacity: .85; }
.nav-link.active .nav-icon { opacity: 1; }
.nav-label { display: inline; }
.icon { font-size: 14px; width: 16px; text-align: center; }

/* right-hand cluster of the masthead */
.sidebar-footer {
  margin: 0 0 0 auto;
  padding: 0;
  border: 0;
  display: flex; align-items: center; gap: var(--s3);
  flex: none;
}
.user-info {
  font-size: 11px; color: var(--text-muted); margin: 0;
  font-family: var(--font-mono); letter-spacing: .04em;
  white-space: nowrap;
}
.btn-logout {
  width: auto; padding: 0 10px; height: 28px;
  background: transparent;
  border: 1px solid var(--border-hi);
  border-radius: var(--r-sm);
  color: var(--text-secondary);
  font-family: var(--font-ui);
  font-size: 11px; font-weight: 500;
  letter-spacing: .06em; text-transform: uppercase;
  cursor: pointer;
  transition: color .14s, border-color .14s, background .14s;
  white-space: nowrap;
}
.btn-logout:hover { color: var(--accent); border-color: var(--accent-line); background: var(--accent-soft); }

/* credit line: only has room in the mobile drawer */
.credit { display: none; font-size: 9px; color: var(--text-muted); letter-spacing: .04em; }

/* the Revenue Tracker link lives inline in the masthead, styled as a nav pill
   with a lime outline so it reads as "leaves this app" rather than a section */
.sidebar > div[style] { flex: none; padding: 0 !important; }
.tracker-link {
  display: flex; align-items: center; gap: 7px;
  height: 28px; padding: 0 11px;
  border: 1px solid var(--accent-line);
  border-radius: var(--r-sm);
  color: var(--accent);
  text-decoration: none;
  font-size: 11px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  white-space: nowrap;
  transition: background .14s;
}
.tracker-link:hover { background: var(--accent-soft); }

/* ── TIER 2: context bar ── */
.main-content { flex: 1; margin-left: 0; display: flex; flex-direction: column; min-width: 0; }

.topbar {
  display: flex; align-items: center; gap: var(--s4);
  height: var(--ctx-h);
  padding: 0 var(--s6);
  background: var(--bg-primary);
  border-bottom: 1px solid var(--border);
  position: sticky; top: var(--nav-h); z-index: 40;
}

/* The page title is set in the brand's voice: cream, uppercase, wide-tracked. */
.topbar h1 {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--text-primary);
  flex: none;
  position: relative;
  padding-left: 14px;
}
/* a lime tick marks the live page */
.topbar h1::before {
  content: '';
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 5px; height: 5px; background: var(--accent);
}

.cmdbar {
  flex: 1; max-width: 460px;
  display: flex; align-items: center; gap: var(--s2);
  height: 28px; padding: 0 10px;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text-muted);
  font-size: 11.5px;
  font-family: var(--font-ui);
  cursor: text;
  transition: border-color .15s;
}
.cmdbar:hover { border-color: var(--border-hi); }
.cmdbar kbd {
  margin-left: auto; font-size: 10px; padding: 1px 4px;
  background: var(--raise); border: 1px solid var(--border-hi);
  border-radius: 2px; color: var(--text-secondary);
}

.menu-toggle { display: none; background: none; border: none; color: var(--text-primary); font-size: 18px; cursor: pointer; }

.topbar-actions {
  display: flex; align-items: center; gap: 7px;
  margin-left: auto;
  font-size: 10px; font-weight: 600;
  color: var(--text-secondary);
  letter-spacing: .16em; text-transform: uppercase;
  white-space: nowrap;
}

.app-content { padding: var(--s6); flex: 1; }

/* ═══════════ PANELS ═══════════ */
.card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 0;
  margin-bottom: var(--s4);
  overflow: hidden;
}

.card-header {
  display: flex; justify-content: space-between; align-items: center;
  gap: var(--s4);
  margin: 0;
  padding: 0 var(--s6);
  height: 48px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-secondary);
}

.card-title {
  font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .18em;
  color: var(--text-primary);
}

/* card body padding is delegated so tables can run edge to edge */
.card > *:not(.card-header):not(.table-container) { padding-left: var(--s6); padding-right: var(--s6); }
.card > *:not(.card-header):not(.table-container):first-child { padding-top: var(--s6); }
.card > *:not(.card-header):not(.table-container):last-child { padding-bottom: var(--s6); }

/* ═══════════ KPI BAND — big cream numerals on black, rules between ═══════════ */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 0;
  margin-bottom: var(--s6);
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow: hidden;
  background: var(--bg-card);
}

/* A grid/flex child defaults to min-width:auto, so it refuses to shrink below its
   content. The automation cards hold a 581px table inside a .stats-grid that is
   overflow:hidden, so on a phone the card stayed 583px wide, 206px of it was clipped
   with nothing able to scroll, and the ON toggles plus "Test now" could not be
   reached at all. min-width:0 lets the column shrink so the inner
   .table-container's own overflow-x:auto actually engages. No effect above ~600px. */
.stats-grid > * { min-width: 0; }
.card { min-width: 0; }
.card *:is(div, section) { min-width: 0; }

.stat-card {
  background: transparent;
  border: 0;
  border-right: 1px solid var(--border);
  border-radius: 0;
  padding: var(--s4) var(--s6) 18px;
  transition: background .14s;
}
.stat-card:last-child { border-right: 0; }
.stat-card:hover { background: var(--glass); }

.stat-card .label {
  font-size: 9.5px; font-weight: 600; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .18em;
}
.stat-card .value {
  font-family: var(--font-mono);
  font-size: 30px; font-weight: 500;
  margin-top: 10px;
  letter-spacing: -.04em;
  line-height: 1;
  color: var(--text-primary);
}
/* KPI numerals stay ivory. Three headline figures in three different hues is a
   rainbow, not a hierarchy — the label already says what each one is. */
.stat-card .value.accent,
.stat-card .value.success,
.stat-card .value.warning,
.stat-card .value.info { color: var(--text-primary); }

/* ═══════════ DATA GRID ═══════════ */
.table-container { overflow-x: auto; -webkit-overflow-scrolling: touch; }

table { width: 100%; border-collapse: collapse; }

th, td {
  padding: 0 var(--s4);
  height: 40px;
  text-align: left;
  border-bottom: 1px solid var(--border-2);
  white-space: nowrap;
}
th:first-child, td:first-child { padding-left: var(--s6); }
th:last-child, td:last-child { padding-right: var(--s6); }

td { font-size: 12px; color: var(--text-secondary); }
td:first-child { color: var(--text-primary); }

th {
  font-family: var(--font-ui);
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: .18em;
  color: var(--text-muted);
  font-weight: 600;
  background: var(--bg-card);
  border-bottom: 1px solid var(--border);
  height: 36px;
  position: sticky; top: 0; z-index: 1;
}

td.num, th.num { text-align: right; }

tbody tr { transition: background .12s; }
tbody tr:hover { background: var(--glass); }
tbody tr:hover td:first-child { color: var(--accent); }
tbody tr:last-child td { border-bottom: 0; }

/* ═══════════ CONTROLS ═══════════
   THE BUTTON WALL WAS THE SINGLE BIGGEST CHEAPENING FORCE.

   Six filled, saturated buttons per row x 13 rows put ~78 competing colour
   blocks on one screen. When everything is saturated nothing is emphasised —
   it reads as noise, not hierarchy. So:

     · Row actions are GHOSTS: transparent, secondary ink, no border. Intent
       appears on HOVER, never at rest.
     · Destructive actions earn colour only on hover, and only as muted rose.
     · Exactly ONE filled button belongs on a screen: .btn-primary, the single
       page-level action (e.g. "+ New Campaign").
     · Semantic fills are retired to tinted outlines; the STATUS column already
       carries that meaning, so the button need not shout it too.
   ═══════════════════════════════════════════════════════════════════════ */
.btn {
  padding: 0 11px;
  height: 30px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .01em;
  transition: background .14s, border-color .14s, color .14s;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  white-space: nowrap;
}
.btn:hover { background: var(--raise); color: var(--text-primary); }

/* the one filled button on the page */
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-ink); }

/* everything below is a ghost at rest; colour is a hover affordance only */
.btn-secondary { background: transparent; color: var(--text-secondary); border-color: var(--border); }
.btn-secondary:hover { background: var(--raise); color: var(--text-primary); border-color: var(--border-hi); }

.btn-accent { background: transparent; border-color: transparent; color: var(--accent); }
.btn-accent:hover { background: var(--accent-soft); color: var(--accent); }

.btn-success { background: transparent; border-color: transparent; color: var(--text-secondary); }
.btn-success:hover { background: var(--ok-bg); color: var(--success); }

.btn-warning { background: transparent; border-color: transparent; color: var(--text-secondary); }
.btn-warning:hover { background: var(--warn-bg); color: var(--warning); }

.btn-danger { background: transparent; border-color: transparent; color: var(--text-secondary); }
.btn-danger:hover { background: var(--bad-bg); color: var(--error); }

.btn-test { background: transparent; border-color: transparent; color: var(--text-secondary); }
.btn-test:hover { background: var(--raise); color: var(--text-primary); }

.btn-clone { background: transparent; border-color: transparent; color: var(--text-secondary); }
.btn-clone:hover { background: var(--raise); color: var(--text-primary); }

.btn-sm { height: 26px; padding: 0 8px; font-size: 11.5px; }
.btn-group { display: flex; gap: 2px; }

/* Row actions are borderless as a SET. .btn-secondary legitimately carries a
   border at page level (Export All / Import), but inside a row that single
   outline made "Edit" look like the important one among six equals. */
.btn-group .btn-sm { border-color: transparent; }
.btn-group .btn-sm:hover { border-color: transparent; }

/* ═══════════ FORMS ═══════════ */
.form-group { margin-bottom: var(--s4); }
.form-group label {
  display: block; margin-bottom: 6px;
  font-size: 9.5px; font-weight: 600; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: .18em;
}
.form-row { display: flex; gap: var(--s4); }
.form-row .form-group { flex: 1; }

input, select, textarea {
  width: 100%;
  padding: 0 11px;
  height: 34px;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--text-primary);
  font-size: 12.5px;
  outline: none;
  transition: border-color .15s, box-shadow .15s;
}
input:hover, select:hover, textarea:hover { border-color: var(--border-hi); }
input:focus, select:focus, textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
input::placeholder, textarea::placeholder { color: var(--text-muted); font-family: var(--font-ui); }

/* The rule above is written for TEXT fields, and `input` matches checkboxes too:
   width:100% inside a 40px cell, minus 22px of horizontal padding and the border,
   resolved to a 0px-wide box. The row-select checkboxes existed in the DOM, were
   `visibility: visible`, and could not be seen or clicked — so multi-select and
   every bulk action were unreachable. Elsewhere in the app this was patched one
   control at a time with an inline style="width:auto"; fixing it here means a new
   checkbox is correct by default instead of needing to remember the workaround.
   Specificity (0,1,1) beats the bare `input` rule, so order does not matter. */
input[type="checkbox"], input[type="radio"] {
  width: 16px; min-width: 16px; height: 16px;
  padding: 0;
  accent-color: var(--accent);
  cursor: pointer;
  vertical-align: middle;
  flex: none;
}
/* ...including under the 560px rule that grows inputs to a touch target. */
@media (max-width: 560px) {
  input[type="checkbox"], input[type="radio"] { width: 18px; min-width: 18px; height: 18px; }
}
textarea { min-height: 86px; padding: 10px 11px; height: auto; resize: vertical; font-size: 12px; }

/* ═══════════ STATUS ═══════════ */
.badge {
  display: inline-block;
  padding: 2px 7px;
  border-radius: 2px;
  font-family: var(--font-ui);
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
  border: 1px solid transparent;
}
.badge-active { background: var(--ok-bg); color: var(--success); border-color: var(--ok-ln); }
.badge-paused { background: var(--warn-bg); color: var(--warning); border-color: var(--warn-ln); }
.badge-stopped { background: var(--bad-bg); color: var(--error); border-color: var(--bad-ln); }

.status-dot { width: 6px; height: 6px; border-radius: 50%; display: inline-block; }
.status-dot.green { background: var(--accent); box-shadow: 0 0 8px var(--accent); }
.status-dot.yellow { background: var(--warning); }
.status-dot.red { background: var(--error); }

/* ═══════════ TABS ═══════════ */
.tabs { display: flex; border-bottom: 1px solid var(--border); margin-bottom: var(--s6); gap: var(--s6); }
.tab {
  padding: 0 0 11px;
  cursor: pointer;
  color: var(--text-muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color .15s, border-color .15s;
  font-size: 10.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .16em;
}
.tab:hover { color: var(--text-secondary); }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); }

.tab-content { display: none; }
.tab-content.active { display: block; }

/* ═══════════ MODAL ═══════════ */
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,.82);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  padding: var(--s6);
  display: flex; align-items: center; justify-content: center;
  z-index: 1000;
}
.modal {
  background: var(--bg-card);
  border: 1px solid var(--border-hi);
  border-radius: var(--r);
  width: 90%; max-width: 620px; max-height: 85vh;
  overflow-y: auto;
}
.modal-header {
  display: flex; justify-content: space-between; align-items: center;
  padding: 0 var(--s6); height: 48px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-secondary);
  position: sticky; top: 0; z-index: 2;
}
.modal-header h3 { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .18em; }
.modal-close { background: none; border: none; color: var(--text-muted); font-size: 21px; cursor: pointer; line-height: 1; transition: color .15s; }
.modal-close:hover { color: var(--accent); }
.modal-body { padding: var(--s6); }

/* ═══════════ TOAST ═══════════ */
#toast-container { position: fixed; top: var(--s4); right: var(--s4); z-index: 2000; display: flex; flex-direction: column; gap: var(--s2); }
.toast {
  padding: 11px 14px;
  border-radius: var(--r-sm);
  font-size: 12px; font-weight: 500;
  animation: slideIn .2s cubic-bezier(.2,.8,.2,1);
  min-width: 250px;
  background: var(--overlay);
  border: 1px solid var(--border-hi);
  border-left: 3px solid var(--text-secondary);
  color: var(--text-primary);
}
.toast.success { border-left-color: var(--accent); }
.toast.error { border-left-color: var(--critical); }
.toast.info { border-left-color: var(--info); }
.toast.warning { border-left-color: var(--warning); }
@keyframes slideIn { from { transform: translateX(10px); opacity: 0; } to { transform: translateX(0); opacity: 1; } }

/* ═══════════ REDIRECT CHAIN ═══════════ */
.redirect-chain { background: var(--bg-input); border: 1px solid var(--border); border-radius: var(--r-sm); padding: var(--s4); max-height: 500px; overflow-y: auto; }
.hop { display: flex; align-items: flex-start; gap: var(--s3); padding: 10px 0; border-bottom: 1px solid var(--border-2); }
.hop:last-child { border-bottom: none; }
.hop-number {
  background: transparent; border: 1px solid var(--accent-line); color: var(--accent);
  width: 22px; height: 22px; border-radius: 2px;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 600; flex-shrink: 0;
}
.hop-details { flex: 1; min-width: 0; }
.hop-url { font-family: var(--font-mono); font-size: 11.5px; color: var(--accent); word-break: break-all; margin-bottom: 3px; }
.hop-status { font-size: 10.5px; color: var(--text-muted); font-family: var(--font-mono); }
.hop-params { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
.param-tag { background: var(--raise); color: var(--text-secondary); padding: 1px 6px; border-radius: 2px; font-size: 10px; border: 1px solid var(--border); }

/* ═══════════ STATES ═══════════ */
.empty-state { text-align: center; padding: var(--s12) var(--s6); color: var(--text-muted); }
.empty-state h3 { margin-bottom: var(--s2); color: var(--text-secondary); font-size: 13px; font-weight: 600; text-transform: uppercase; letter-spacing: .14em; }
.loading { display: flex; align-items: center; justify-content: center; padding: var(--s8); }
.spinner { width: 22px; height: 22px; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ═══════════ TEST REPORT ═══════════ */
.test-report { padding: 2px 0; }
.test-report-header { display: flex; align-items: center; gap: 10px; padding: 11px 14px; border-radius: var(--r-sm); margin-bottom: var(--s6); border: 1px solid; }
.test-report-header.success { background: var(--ok-bg); border-color: var(--ok-ln); }
.test-report-header.error { background: var(--bad-bg); border-color: var(--bad-ln); }
.test-report-header .check { font-size: 16px; }
.test-report-header.success .check { color: var(--accent); }
.test-report-header.error .check { color: var(--error); }

.report-section { margin-bottom: var(--s4); }
.report-section-title { font-size: 9.5px; text-transform: uppercase; letter-spacing: .18em; color: var(--text-muted); margin-bottom: 6px; font-weight: 600; }
.report-value {
  background: var(--bg-input); border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 9px 11px; font-family: var(--font-mono); font-size: 11.5px;
  color: var(--accent); word-break: break-all; line-height: 1.6;
}
.report-value.highlight { border-color: var(--ok-ln); background: var(--ok-bg); color: var(--success); }
.report-value.bold-text { font-family: var(--font-ui); font-weight: 700; font-size: 14px; color: var(--text-primary); }

.report-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0; margin-bottom: var(--s4); border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; }
.report-grid-item { background: var(--bg-card); padding: 11px 13px; border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); }
.report-grid-item .grid-label { font-size: 9px; text-transform: uppercase; letter-spacing: .18em; color: var(--text-muted); margin-bottom: 4px; }
.report-grid-item .grid-value { font-family: var(--font-mono); font-size: 13px; font-weight: 500; color: var(--text-primary); }
.report-grid-item .grid-sub { font-size: 10px; color: var(--text-muted); margin-top: 2px; }

.chain-step { display: flex; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--border-2); }
.chain-step:last-child { border-bottom: none; }
.chain-num { display: flex; align-items: center; gap: 6px; min-width: 56px; }
.chain-num span:first-child { color: var(--text-muted); font-family: var(--font-mono); font-size: 11.5px; }

.status-badge { display: inline-block; padding: 1px 6px; border-radius: 2px; font-family: var(--font-mono); font-size: 10px; font-weight: 600; }
.status-badge.s3xx { background: var(--warn-bg); color: var(--warning); }
.status-badge.s2xx { background: var(--accent-soft); color: var(--accent); }
.status-badge.s4xx { background: var(--bad-bg); color: var(--error); }
.status-badge.s5xx { background: rgba(144,133,233,.16); color: #9085e9; }

.chain-url { font-family: var(--font-mono); font-size: 11px; color: var(--text-secondary); word-break: break-all; line-height: 1.5; flex: 1; }

.modal.wide { max-width: 900px; }
.bulk-textarea { min-height: 120px; font-size: 11.5px; }

/* .btn-test / .btn-clone are defined once, in CONTROLS. An earlier copy here
   re-introduced a purple and a blue outline and won on source order, which is
   why two of six row actions wore borders nobody asked for. */

/* ═══════════════════ RESPONSIVE ═══════════════════
   Below 880px the masthead collapses back into an off-canvas drawer, because a
   horizontal nav cannot hold ten destinations on a phone.
   ═══════════════════════════════════════════════════ */
.sidebar-backdrop {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(0,0,0,.66);
  opacity: 0; pointer-events: none; transition: opacity .22s ease;
}
body.nav-open .sidebar-backdrop { opacity: 1; pointer-events: auto; }

@media (max-width: 1180px) {
  .sidebar { gap: var(--s4); padding: 0 var(--s4); }
  .nav-link { padding: 0 9px; }
  .user-info { display: none; }
  .app-content { padding: var(--s4); }
  .topbar { padding: 0 var(--s4); }
}

@media (max-width: 880px) {
  .sidebar {
    position: fixed; top: 0; left: 0;
    width: min(280px, 82vw); height: 100vh;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 0 0 var(--s4);
    border-bottom: 0;
    border-right: 1px solid var(--border);
    transform: translateX(-100%);
    overflow-y: auto;
    padding-left: var(--safe-l);
  }
  .sidebar.open { transform: translateX(0); }
  .sidebar-header { height: var(--nav-h); padding: 0 var(--s4); border-bottom: 1px solid var(--border); flex: none; }
  .nav-links { flex-direction: column; align-items: stretch; gap: 1px; padding: var(--s2) var(--s2); flex: none; overflow: visible; }
  .nav-link { height: 42px; padding: 0 12px; font-size: 13px; letter-spacing: 0; text-transform: none; }
  .nav-icon { display: block; width: 17px; height: 17px; }
  .sidebar-footer { margin: auto 0 0; padding: var(--s4); border-top: 1px solid var(--border); flex-direction: column; align-items: stretch; gap: var(--s2); }
  .user-info { display: block; }
  .btn-logout { width: 100%; height: 38px; }
  .credit { display: block; text-align: center; }
  .sidebar > div[style] { padding: 0 var(--s2) !important; }
  .tracker-link { height: 42px; padding: 0 12px; font-size: 13px; letter-spacing: 0; text-transform: none; }

  .main-content { margin-left: 0; }
  .topbar { top: 0; }
  .menu-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; margin-left: -8px;
    border-radius: var(--r-sm);
  }
  .menu-toggle:hover, .menu-toggle:active { background: var(--raise); }
  .cmdbar { display: none; }
  .app-content { padding: var(--s4); padding-bottom: calc(var(--s6) + var(--safe-b)); }
  .stats-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
  .report-grid { grid-template-columns: 1fr; }
  .modal { max-height: 88vh; }
  .modal.wide { max-width: 100%; }
  th { position: static; }
}

@media (max-width: 560px) {
  .app-content { padding: var(--s3); padding-bottom: calc(var(--s6) + var(--safe-b)); }
  .card { margin-bottom: var(--s3); }
  .card-header { height: auto; padding: var(--s3) var(--s4); flex-direction: column; align-items: flex-start; gap: var(--s2); }
  .card-header .btn-group { width: 100%; }
  .card > *:not(.card-header):not(.table-container) { padding-left: var(--s4); padding-right: var(--s4); }

  .stats-grid { grid-template-columns: 1fr 1fr; margin-bottom: var(--s4); }
  /* an odd count would leave a hollow cell sitting alone in the last row */
  .stats-grid > .stat-card:last-child:nth-child(odd) { grid-column: 1 / -1; border-right: 0; }
  .stat-card { padding: var(--s3) var(--s4) var(--s4); border-bottom: 1px solid var(--border); }
  .stat-card .value { font-size: 23px; }

  .form-row { flex-direction: column; gap: 0; }

  /* 16px is the threshold below which iOS Safari zooms the viewport on focus and
     never zooms back out — it must not be reduced to "fit" a small screen. */
  input, select, textarea { font-size: 16px; height: 44px; }
  textarea { height: auto; }

  /* 44px: the Apple HIG minimum for a one-handed tap target. */
  .btn { height: 44px; padding: 0 14px; }
  .btn-sm { height: 36px; padding: 0 11px; }
  .btn-group { flex-wrap: wrap; }
  .btn-group .btn { flex: 1 1 auto; }

  .tabs { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; gap: var(--s4); }
  .tabs::-webkit-scrollbar { display: none; }
  .tab { white-space: nowrap; }

  th, td { height: 44px; padding: 0 11px; }
  th:first-child, td:first-child { padding-left: var(--s4); }

  .modal-overlay { padding: 0; align-items: flex-end; }
  .modal { width: 100%; max-width: 100%; max-height: 92vh; border-radius: 8px 8px 0 0; border-bottom: 0; padding-bottom: var(--safe-b); }
  .modal-body { padding: var(--s4); }
  .modal-header { padding: 0 var(--s4); }

  #toast-container { top: auto; bottom: calc(var(--s3) + var(--safe-b)); left: var(--s3); right: var(--s3); }
  .toast { min-width: 0; width: 100%; }

  .hop { flex-direction: column; gap: 6px; }
  .chain-step { flex-direction: column; gap: 4px; }
  .empty-state { padding: var(--s8) var(--s4); }
}

@media (hover: none) and (pointer: coarse) {
  th, td { height: 46px; }
}

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

/* ═══════════════════════════════════════════════════════════════════════════
   PAGE LAYOUT — restructure 2026-10-18

   The previous screen stacked four bars of chrome, a 120px box holding three
   integers, two separate search fields and a six-button strip repeated on all
   382 rows. The result was a flat wall with no focal point. This replaces it:

     page header (counts inline)  ->  toolbar  ->  sheet
     row actions appear on HOVER; everything else lives in an overflow menu
     content capped at 1440px so columns stop drifting apart on wide screens

   Grouping by "family" was deliberately NOT built: there is no parent field,
   and merging rows because their names look alike would invent structure.
   ═══════════════════════════════════════════════════════════════════════════ */
.page { max-width: 1440px; margin: 0 auto; }

/* ── page header ── */
.page-head { display: flex; align-items: flex-end; gap: var(--s4); min-height: 64px; margin-bottom: var(--s4); }
.page-head-main { min-width: 0; }
.page-title { font-size: 22px; font-weight: 600; letter-spacing: -.02em; color: var(--text-primary); line-height: 1.1; }
.page-sub { display: flex; align-items: center; gap: 9px; margin-top: 6px; font-size: 12px; color: var(--text-muted); }
.page-sub i { width: 3px; height: 3px; border-radius: 50%; background: var(--border-hi); display: block; }
.page-head-actions { margin-left: auto; display: flex; align-items: center; gap: 6px; }

/* ── toolbar / selection bar (the second never coexists with the first) ── */
.toolbar, .selbar {
  display: flex; align-items: center; gap: var(--s3);
  /* --s6 matches the table's first/last cell padding, so the search box lines
     up with the leading column and the controls on the right line up with the
     Actions column. At --s3 they sat 12px inboard of the grid and every row
     below them looked subtly off. */
  height: 48px; padding: 0 var(--s6);
  border: 1px solid var(--border); border-radius: var(--r) var(--r) 0 0;
  border-bottom: 0; background: var(--bg-secondary);
}
.toolbar[hidden], .selbar[hidden] { display: none; }
.toolbar-search { max-width: 320px; height: 30px; font-family: var(--font-ui); font-size: 12.5px; }
.toolbar-right { margin-left: auto; display: flex; align-items: center; gap: var(--s2); }
.selbar { background: var(--accent-soft); border-color: var(--accent-line); }
.selbar-count { font-size: 12.5px; color: var(--text-primary); margin-right: var(--s2); }
.selbar-count b { font-variant-numeric: tabular-nums; }
.hidden-sel { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

/* segmented control */
.seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; }
.seg button {
  font-family: var(--font-ui); font-size: 12px; font-weight: 500;
  height: 28px; padding: 0 11px; border: 0; background: transparent;
  color: var(--text-secondary); cursor: pointer; transition: background .14s, color .14s;
  display: inline-flex; align-items: center; gap: 6px;
}
.seg button + button { border-left: 1px solid var(--border); }
.seg button:hover { color: var(--text-primary); background: var(--raise); }
.seg button.on { background: var(--raise); color: var(--text-primary); }
.seg button b { font-weight: 500; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.seg button.on b { color: var(--accent); }
.seg.density button { padding: 0 9px; font-size: 13px; }

/* ── the sheet ── */
.sheet { border: 1px solid var(--border); border-radius: 0 0 var(--r) var(--r); background: var(--bg-card); }
.table-foot { padding: 10px 2px; font-size: 11.5px; color: var(--text-muted); }

table.grid { table-layout: fixed; }
table.grid th { height: 40px; background: var(--bg-card); }
table.grid td { height: 48px; }
table.grid.compact td { height: 36px; }
table.grid td, table.grid th { overflow: hidden; }
.ta-r { text-align: right; }
.dash { color: var(--text-muted); }

/* campaign cell: one line, name is the affordance, id is secondary context */
.cell-name { min-width: 0; }
.name-link {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--font-ui); font-size: 13.5px; font-weight: 500;
  color: var(--text-primary); max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: inline-block;
  vertical-align: middle; text-align: left;
}
.name-link:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.name-id { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); margin-left: 9px; vertical-align: middle; }
.cell-market { font-size: 12.5px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; }
.cell-date { font-size: 12px; color: var(--text-secondary); font-variant-numeric: tabular-nums; }

/* ── the Google Ads wiring cell ──────────────────────────────────────────────
   Account above, the reference below it. The ref is monospaced because it is an
   identifier people compare character by character against the Ads UI, and it
   carries its level: c<id> is a campaign, ag<id> an ad group. */
/* The renamed accounts run to 29 characters ("SHEIN FR-US-CA-AU AUD/UNKNOWN"),
   which needs 222px of text. The default 16px cell padding ate 32 of them, so this
   cell is tighter and a touch smaller than its neighbours - the alternative was a
   column wide enough to squeeze the campaign name, or a truncated account, and a
   truncated account defeats the point of the column. */
.cell-wired { min-width: 0; padding-left: 12px; padding-right: 12px; }
.wired {
  display: block; width: 100%; min-width: 0;
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--font-ui); text-align: left;
}
.wired-acct, .wired-ref {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wired-acct { font-size: 12px; color: var(--text-secondary); }
/* wired, but to an account ASE has no customer id for - muted and italic so it
   does not read as the account's actual name */
.wired-acct.wired-none { color: var(--text-muted); font-style: italic; }
.wired-ref {
  font-family: var(--font-mono); font-size: 10.5px; color: var(--text-muted);
  margin-top: 1px;
}
.wired:hover .wired-acct { color: var(--accent); }
.grp-acct {
  font-size: 11.5px; color: var(--text-muted);
  padding-left: 9px; border-left: 1px solid var(--border);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── grouped campaign rows: Offer · Geo · Device ──────────────────────────────
   The header is a row in the same table, so it inherits the colgroup widths and an
   expanded group stays aligned with the rows above it. It reads as a divider, not
   as another card: a tinted band with a weightier label, no border or radius of
   its own. */
tr.grp-row > td {
  background: var(--glass);
  border-bottom: 1px solid var(--border);
  height: 42px;
}
table.grid.compact tr.grp-row > td { height: 34px; }
.cell-grp { min-width: 0; }
.grp-toggle {
  display: inline-flex; align-items: center; gap: 9px;
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--font-ui); font-size: 13px; color: var(--text-primary);
  max-width: 100%; min-width: 0; text-align: left;
}
.grp-chev {
  display: inline-block; width: 10px; flex: none;
  color: var(--text-muted); font-size: 10px;
  transition: transform .14s ease;
}
tr.grp-row.open .grp-chev { transform: rotate(90deg); }
.grp-toggle:hover .grp-chev, .grp-toggle:hover .grp-offer { color: var(--accent); }
.grp-offer { font-weight: 600; letter-spacing: .01em; transition: color .14s; }
/* Geo and device sit between hairlines rather than behind pills: three filled
   chips per header turns a 40-row table into confetti. */
.grp-geo, .grp-dev {
  font-size: 12px; color: var(--text-secondary);
  padding-left: 9px; border-left: 1px solid var(--border);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.grp-dev { color: var(--text-muted); }
.grp-n {
  font-family: var(--font-mono); font-size: 11px; color: var(--text-muted);
  font-variant-numeric: tabular-nums; flex: none;
  padding: 1px 6px; border-radius: 999px; background: var(--raise);
}
.grp-meta {
  font-size: 11.5px; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* A member row is indented so the group it belongs to is readable at a glance even
   when the header has scrolled off. */
/* Two levels, each a step further in. Level two is lighter and smaller so the
   hierarchy reads at a glance instead of looking like two kinds of divider. */
tr.grp-row.grp-l2 > td { background: transparent; }
tr.grp-row.grp-l2 > td:first-child { padding-left: 28px; }
tr.grp-row.grp-l2 .grp-toggle { font-size: 12.5px; padding-left: 16px; }
.grp-kind { font-weight: 500; color: var(--text-secondary); transition: color .14s; }
tr.grp-row.grp-l2 .grp-toggle:hover .grp-kind { color: var(--accent); }
tr.grp-row.grp-l2 .grp-dev {
  font-size: 12px; color: var(--text-muted);
  padding-left: 9px; border-left: 1px solid var(--border);
}

tr.camp-row.in-grp > td:nth-child(2) { padding-left: 46px; }
tr.camp-row.in-grp > td:first-child { padding-left: 34px; }

@media (max-width: 760px) {
  /* The device is the first thing to go: it is repeated down every row of the
     group anyway, where the full campaign name carries it. The account summary
     goes with it - every row in the group has the account in its own column. */
  .grp-acct { display: none; }
  tr.camp-row.in-grp > td:nth-child(2) { padding-left: 30px; }
  tr.grp-row.grp-l2 > td:first-child { padding-left: 20px; }
  tr.grp-row.grp-l2 .grp-toggle { padding-left: 10px; }
}

/* ── row actions: hidden until the row is hovered, space always reserved ── */
.cell-actions { text-align: right; }
.row-acts { display: inline-flex; align-items: center; gap: 2px; justify-content: flex-end; }
.act {
  width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid transparent; border-radius: var(--r-sm);
  color: var(--text-muted); cursor: pointer; font-size: 11px; line-height: 1;
  opacity: 0; transition: opacity .12s, background .14s, color .14s;
}
.act.always { opacity: .55; font-size: 15px; }
tr:hover .act, tr:focus-within .act { opacity: 1; }
.act:hover { background: var(--raise); color: var(--text-primary); }

/* ── overflow menus ── */
.menu-wrap { position: relative; display: inline-flex; }
.btn-icon { width: 30px; padding: 0; font-size: 15px; color: var(--text-secondary); }
.btn-ghost { border-color: transparent; color: var(--text-muted); }
.btn-ghost:hover { color: var(--text-primary); }
.menu {
  position: absolute; right: 0; top: calc(100% + 5px);
  min-width: 164px; padding: 5px;
  background: var(--overlay); border: 1px solid var(--border-hi);
  border-radius: var(--r); z-index: 300;
  display: none; text-align: left;
  box-shadow: 0 18px 40px -16px rgba(0,0,0,.8);
}
.menu.open { display: block; }
.menu button {
  display: block; width: 100%; text-align: left;
  background: none; border: 0; cursor: pointer;
  font-family: var(--font-ui); font-size: 12.5px; color: var(--text-secondary);
  padding: 7px 10px; border-radius: var(--r-sm); transition: background .12s, color .12s;
}
.menu button:hover { background: var(--raise); color: var(--text-primary); }
.menu button.danger:hover { background: var(--bad-bg); color: var(--error); }
.menu-sep { height: 1px; background: var(--border); margin: 5px 2px; }
/* a row's menu must escape the table's clipping */
.sheet { overflow: visible; }
.table-container.sheet { overflow-x: auto; }

@media (max-width: 880px) {
  .page-head { flex-direction: column; align-items: flex-start; gap: var(--s2); }
  .page-head-actions { margin-left: 0; }
  .toolbar { flex-wrap: wrap; height: auto; padding: var(--s2) var(--s3); gap: var(--s2); }
  .toolbar-search { max-width: 100%; flex: 1 1 180px; }
  .toolbar-right { margin-left: 0; }
  .act { opacity: 1; }
}

/* ── chrome reduction: the context bar duplicated the page title ──────────────
   The page header now states which page you are on and its counts, so the
   middle bar was a third tier of chrome repeating "Campaigns" and holding a
   second search. On desktop it is removed entirely; global search and the
   connection light move into the masthead. It survives below 880px, where it
   still carries the drawer toggle and the page name. */
@media (min-width: 881px) {
  .topbar { display: none; }
  .sidebar-footer .conn { display: inline-flex; align-items: center; padding: 0 2px; }
  .sidebar-footer #global-search { color: var(--text-muted); }
  .sidebar-footer #global-search:hover { color: var(--text-primary); background: var(--raise); }
  .app-content { padding-top: var(--s8); }
}
@media (max-width: 880px) {
  .sidebar-footer .conn, .sidebar-footer #global-search { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   ROLL-OUT: the campaign-page treatment applied to every other page.

   These are deliberately global rules rather than ten per-page rewrites —
   the same three faults recurred on every screen (an oversized KPI band,
   columns drifting apart on wide monitors, and inconsistent row rhythm).
   ═══════════════════════════════════════════════════════════════════════════ */

/* 1. Cap every page, not just Campaigns. On a 2560px monitor an uncapped table
      strands its columns at opposite edges of the screen. */
.app-content > * { max-width: 1440px; margin-left: auto; margin-right: auto; }
.app-content > .page { max-width: 1440px; }

/* 2. The KPI band becomes a slim instrument strip. It previously spent ~120px
      of vertical space to show three integers, pushing the actual content of
      every page below the fold. */
.stats-grid {
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  margin-bottom: var(--s4);
}
.stat-card { padding: 12px var(--s4) 13px; }
.stat-card .label { font-size: 9px; letter-spacing: .16em; }
.stat-card .value { font-size: 20px; margin-top: 5px; font-weight: 500; }

/* 3. One row rhythm for every table in the product, not only .grid. */
table th { height: 40px; }
table td { height: 48px; }
table.compact td { height: 36px; }

/* 4. Page header spacing is owned by the header itself, so a page that renders
      a bare card directly still starts on the same baseline. */
.app-content > .page-head { margin-bottom: var(--s4); }

/* 5. Cards sit flush under a header; the doubled gap read as a layout slip. */
.page-head + .card, .page-head + .stats-grid { margin-top: 0; }

/* ── Dashboard / shared page furniture ─────────────────────────────────────── */
.toolbar-label { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .18em; color: var(--text-primary); }
.sub-warn { color: var(--warning); }

/* plain (non-clickable) identifier cell, same rhythm as .name-link */
.name-plain {
  font-size: 13.5px; font-weight: 500; color: var(--text-primary);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  display: inline-block; vertical-align: middle;
}
.cell-code {
  font-family: var(--font-mono); font-size: 11.5px; color: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cell-num { font-family: var(--font-mono); font-size: 12px; color: var(--text-secondary); }

/* a quiet panel — lighter than a .card, for secondary surfaces like the feed */
.panel { border: 1px solid var(--border); border-radius: var(--r); background: var(--bg-card); margin-top: var(--s4); overflow: hidden; }
.panel-head {
  display: flex; align-items: center; justify-content: space-between;
  height: 44px; padding: 0 var(--s4);
  border-bottom: 1px solid var(--border); background: var(--bg-secondary);
}
.panel-title { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .18em; color: var(--text-primary); }
.live-dot { display: inline-flex; align-items: center; gap: 7px; font-size: 10px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; color: var(--text-muted); }
.feed { font-family: var(--font-mono); font-size: 11.5px; max-height: 220px; overflow-y: auto; padding: var(--s3) var(--s4); }
.feed-empty { color: var(--text-muted); }

/* the dashboard's toolbar sits directly on the sheet, like the campaign page */
.toolbar + .table-container.sheet { border-top: 0; }


/* ─── Theme switch ───────────────────────────────────────────────────────── */
.theme-sw { display: inline-flex; border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden; }
.theme-sw button {
  width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; cursor: pointer; color: var(--text-muted);
  transition: background .14s, color .14s;
}
.theme-sw button + button { border-left: 1px solid var(--border); }
.theme-sw button:hover { color: var(--text-primary); background: var(--raise); }
.theme-sw button.on { background: var(--accent); color: var(--accent-ink); }
.theme-sw svg { width: 14px; height: 14px; fill: currentColor; display: block; }

/* ═══════════════════════════════════════════════════════════════════════════
   LAYOUT MODES — set on <html data-layout="...">, applied to the SAME markup.
   Default (no attribute) = 01 Top Rail. These two restyle the existing shell
   rather than re-render it, so every page and component keeps working.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── Layout modes are DESKTOP-ONLY ─────────────────────────────────────────
   `:root[data-layout="islands"] .sidebar` outranks the mobile drawer rule, so
   on a phone the drawer stayed a 50px pill showing only the brand mark and no
   navigation at all. The switcher is already hidden below 1180px, so the modes
   are scoped to desktop and small screens always get the standard shell. */
@media (min-width: 881px) {
  /* ─── 09 EDITORIAL REPORT ───────────────────────────────────────────────────
     No boxes. Hairlines and type hierarchy carry the structure; the sheet loses
     its border entirely and the rows become a ruled list. Serif display face. */
  :root[data-layout="editorial"] body { font-family: 'Hanken Grotesk', var(--font-ui); font-size: 14.5px; }
  :root[data-layout="editorial"] .page { max-width: 1040px; }
  :root[data-layout="editorial"] .app-content { padding: var(--s12) var(--s8); }

  :root[data-layout="editorial"] .sidebar { background: transparent; border-bottom: 1px solid var(--border); height: 52px; }
  :root[data-layout="editorial"] .sidebar-header h2 { font-family: 'Fraunces', serif; font-weight: 600; letter-spacing: .04em; text-transform: none; font-size: 17px; }
  :root[data-layout="editorial"] .logo-icon-sm { background: transparent; color: var(--accent); border: 1px solid var(--accent-line); }
  :root[data-layout="editorial"] .nav-link { font-size: 12.5px; letter-spacing: .02em; }
  :root[data-layout="editorial"] .nav-link.active { background: transparent; color: var(--accent); font-weight: 600; box-shadow: inset 0 -2px 0 var(--accent); border-radius: 0; }

  :root[data-layout="editorial"] .page-title { font-family: 'Fraunces', serif; font-size: 36px; font-weight: 600; letter-spacing: -.02em; }
  :root[data-layout="editorial"] .page-head { align-items: flex-end; border-bottom: 1px solid var(--border); padding-bottom: var(--s4); min-height: 0; }
  :root[data-layout="editorial"] .page-sub { font-size: 13px; }

  :root[data-layout="editorial"] .toolbar,
  :root[data-layout="editorial"] .selbar { background: transparent; border: 0; border-bottom: 1px solid var(--border-2); border-radius: 0; padding: 0; height: 56px; }
  :root[data-layout="editorial"] .toolbar-search { background: transparent; border: 0; border-bottom: 1px solid var(--border); border-radius: 0; padding-left: 0; }
  :root[data-layout="editorial"] .seg { border: 0; }
  :root[data-layout="editorial"] .seg button.on { background: transparent; color: var(--accent); box-shadow: inset 0 -2px 0 var(--accent); }

  :root[data-layout="editorial"] .sheet,
  :root[data-layout="editorial"] .card,
  :root[data-layout="editorial"] .panel { background: transparent; border: 0; border-radius: 0; }
  :root[data-layout="editorial"] .card-header,
  :root[data-layout="editorial"] .panel-head { background: transparent; border-bottom: 1px solid var(--border); padding-left: 0; padding-right: 0; }
  :root[data-layout="editorial"] table.grid th { background: transparent; border-bottom: 1px solid var(--border); }
  :root[data-layout="editorial"] th:first-child, :root[data-layout="editorial"] td:first-child { padding-left: 0; }
  :root[data-layout="editorial"] th:last-child,  :root[data-layout="editorial"] td:last-child  { padding-right: 0; }
  :root[data-layout="editorial"] table.grid td { height: 58px; border-bottom: 1px solid var(--border-2); }
  :root[data-layout="editorial"] .name-link { font-family: 'Fraunces', serif; font-size: 16px; font-weight: 600; }
  :root[data-layout="editorial"] .stats-grid { border: 0; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); border-radius: 0; background: transparent; }
  :root[data-layout="editorial"] .stat-card { background: transparent; border-right: 0; padding-left: 0; }
  :root[data-layout="editorial"] .stat-card .value { font-family: 'Fraunces', serif; font-size: 30px; font-weight: 600; }

  /* ─── 10 FLOATING ISLANDS ───────────────────────────────────────────────────
     Rounded panels over a tinted canvas, nav collapsed to a floating pill. */
  :root[data-layout="islands"] body {
    font-family: 'Outfit', var(--font-ui);
    background:
      radial-gradient(900px 420px at 18% -8%, color-mix(in srgb, var(--accent) 7%, var(--bg-primary)), transparent 65%),
      var(--bg-primary);
  }
  :root[data-layout="islands"] .sidebar {
    width: calc(100% - 32px); margin: 14px auto 0; height: 50px;
    border: 1px solid var(--border); border-radius: 999px; padding: 0 var(--s4);
    background: var(--bg-card);
  }
  :root[data-layout="islands"] .nav-link { border-radius: 999px; }
  :root[data-layout="islands"] .btn-logout, :root[data-layout="islands"] .tracker-link,
  :root[data-layout="islands"] .theme-sw, :root[data-layout="islands"] .layout-sw { border-radius: 999px; }
  :root[data-layout="islands"] .app-content { padding: var(--s6) var(--s8) var(--s12); }

  :root[data-layout="islands"] .toolbar,
  :root[data-layout="islands"] .selbar { border-radius: 16px; border: 1px solid var(--border); margin-bottom: 12px; height: 54px; padding: 0 var(--s4); }
  :root[data-layout="islands"] .sheet { border-radius: 18px; border: 1px solid var(--border); overflow: hidden; }
  :root[data-layout="islands"] .card, :root[data-layout="islands"] .panel { border-radius: 18px; }
  :root[data-layout="islands"] .stats-grid { border-radius: 16px; gap: 10px; background: transparent; border: 0; }
  :root[data-layout="islands"] .stat-card { border: 1px solid var(--border); border-radius: 14px; background: var(--bg-card); }
  :root[data-layout="islands"] .btn { border-radius: 999px; }
  :root[data-layout="islands"] .toolbar-search { border-radius: 999px; }
  :root[data-layout="islands"] .seg { border-radius: 999px; }
  :root[data-layout="islands"] table.grid td { height: 52px; }
  :root[data-layout="islands"] .page-title { font-weight: 600; letter-spacing: -.01em; }
  :root[data-layout="islands"] .badge { border-radius: 999px; padding: 3px 9px; }
  :root[data-layout="islands"] .cell-date, :root[data-layout="islands"] .name-id { font-family: 'Roboto Mono', var(--font-mono); }

}

/* layout switch — a single icon + menu. A 3-button group was tried first and
   cost ~145px of masthead width, which pushed "Automation" and "Settings" off
   the end of the nav. */
.menu button.on { color: var(--accent); background: var(--accent-soft); }
.menu button.on::after { content: ' ✓'; }

/* The masthead holds 9 destinations plus Tracker, layout, theme, search, account
   and sign-out. Below ~1560px the Tracker pill drops to its icon so the last nav
   item ("Settings") is never clipped; its title attribute keeps it identifiable. */
@media (max-width: 1560px) {
  .tracker-link .nav-label { display: none; }
  .tracker-link { padding: 0 9px; }
}

/* The Tracker tab: its frame owns the full content area and scrolls internally. */
.tracker-frame {
  display: block; width: 100%; border: 0;
  height: calc(100vh - var(--nav-h));
  background: var(--bg-primary);
}
.app-content:has(.tracker-frame) { padding: 0; }
.app-content > .tracker-frame { max-width: none; }
@media (max-width: 880px) { .tracker-frame { height: calc(100vh - var(--ctx-h)); } }

/* ═══════════════════════════════════════════════════════════════════════════
   MOBILE DATA GRID

   The grid uses table-layout:fixed with fixed widths on every column except
   Campaign, which absorbs the remainder. On a 390px phone those fixed widths
   total 522px, so the remainder went NEGATIVE and the Campaign column — the
   only one that identifies the row — collapsed to zero. The table rendered as
   checkbox → Market, with no name anywhere.

   Fix: give the table a real minimum width so every column keeps its size and
   the container scrolls honestly, then FREEZE the checkbox and the name so the
   identifier stays on screen while the numbers scroll under it.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 880px) {
  /* table-layout:fixed + the desktop <colgroup> is what crushed the name column.
     Switching to auto and neutralising the colgroup lets content size the
     columns, with min-width guaranteeing the row still has room to breathe. */
  table.grid { table-layout: auto; min-width: 660px; }
  table.grid colgroup { display: none; }

  /* frozen identifier columns. They need an opaque background or scrolling
     content shows through them. */
  table.grid th:nth-child(1), table.grid td:nth-child(1) {
    position: sticky; left: 0; z-index: 3; background: var(--bg-card);
  }
  table.grid th:nth-child(2), table.grid td:nth-child(2) {
    position: sticky; left: 40px; z-index: 2; background: var(--bg-card);
    box-shadow: 1px 0 0 var(--border-2);
  }
  /* the hover tint cannot paint through a sticky cell, so restate it */
  table.grid tbody tr:hover td:nth-child(1),
  table.grid tbody tr:hover td:nth-child(2) { background: var(--raise); }

  .cell-name { max-width: 230px; }
  .name-link { max-width: 100%; }
}

@media (max-width: 560px) {
  /* Created is the least load-bearing column on a phone: the name, market and
     status answer "which campaign and is it running", the date rarely does. */
  table.grid th:nth-child(5), table.grid td:nth-child(5) { display: none; }
  /* ...but the campaigns table gained a Google Ads column, so Created moved to 6.
     Restate both ends rather than renumbering the generic rule, which other grids
     on five columns still rely on. */
  #campaigns-table th:nth-child(5), #campaigns-table td:nth-child(5) { display: table-cell; }
  #campaigns-table th:nth-child(6), #campaigns-table td:nth-child(6) { display: none; }
  table.grid { min-width: 540px; }
  .cell-name { max-width: 200px; }

  /* one clear primary action; the overflow menu holds the rest */
  .page-head-actions { width: 100%; }
  .page-head-actions .btn-primary { flex: 1; }
}

/* ───────────────────────────── BULK RENAME ──────────────────────────────
   The preview is the whole safety mechanism: it has to make a wrong proposal
   visible at a glance, in a list of 120. So the proposed name is the loudest
   thing in the row, and anything with an unresolved component is tinted and
   carries its reason inline rather than behind a hover. */
.rename-intro p { margin: 0 0 8px; font-size: 13px; line-height: 1.55; }
.rename-intro code { font-family: var(--font-mono); font-size: 12px;
  background: var(--glass); padding: 1px 5px; border-radius: 4px; }

.rn-summary { font-size: 13.5px; padding: 10px 12px; margin: 12px 0 0;
  background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--r-sm); }
.rn-summary .hint { margin-top: 5px; }
.rn-warn { margin-top: 8px; font-size: 12.5px; color: var(--warn, var(--error)); }

.rn-table { max-height: min(56vh, 520px); overflow: auto; margin-top: 4px; }
.rn-table .rn-id { font-family: var(--font-mono); font-size: 11.5px; color: var(--text-muted); }
.rn-table .rn-old { font-size: 12.5px; color: var(--text-muted); }
.rn-table .rn-new code { font-family: var(--font-mono); font-size: 12px;
  color: var(--text); word-break: break-all; }
.rn-table tr.rn-has-issue { background: var(--bad-bg); }
.rn-issue { font-size: 11px; color: var(--error); margin-top: 3px; line-height: 1.4; }

/* On a phone the two name columns cannot sit side by side and stay readable,
   so the row becomes a stack: current name above, proposed name below it. */
@media (max-width: 720px) {
  .rn-table table, .rn-table thead, .rn-table tbody, .rn-table tr, .rn-table td { display: block; width: 100%; }
  .rn-table thead { display: none; }
  .rn-table tr { border-bottom: 1px solid var(--border); padding: 8px 0; position: relative; }
  .rn-table td { border: 0; padding: 2px 0 2px 26px; }
  .rn-table td:first-child { position: absolute; left: 0; top: 10px; width: 20px; padding: 0; }
  .rn-table .rn-id { padding-left: 26px; }
}

/* ── Auto-naming modal ───────────────────────────────────────────────────────
   A proposed name is a claim with evidence behind it, so the dialog is built as
   label/value pairs rather than prose: the two names face each other at the top,
   the parts that produced the new one sit under it, and what was read out of
   Google Ads sits under that. Every colour is a token, so it follows the theme. */
.an-wrap { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.an-wrap .muted { font-size: 12.5px; color: var(--text-muted); margin: 0; line-height: 1.55; }
.an-wrap .mono { font-family: var(--font-mono); font-size: 12.5px; word-break: break-all; }
.an-wrap .error-text { color: var(--error); font-size: 12.5px; margin: 0; }
.an-wrap .warn-text {
  color: var(--warning); font-size: 12px; margin: 0;
  background: var(--warn-bg); border: 1px solid var(--warn-ln);
  border-radius: var(--r-sm); padding: 8px 10px;
}
.an-names, .an-parts, .an-ev {
  display: flex; flex-direction: column; gap: 6px;
  border: 1px solid var(--border); border-radius: var(--r-sm); padding: 11px 12px;
}
.an-parts, .an-ev { gap: 5px; }
/* the label column is fixed so every value starts on one line, however long the label */
.an-wrap .kv { display: flex; gap: 12px; align-items: baseline; min-width: 0; }
.an-wrap .kv > span {
  flex: 0 0 104px; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-muted);
}
.an-wrap .kv > b { flex: 1 1 auto; min-width: 0; font-size: 13px; font-weight: 600; word-break: break-word; }
.an-wrap .kv > b.an-new { color: var(--accent); }
.an-opt { display: flex; gap: 9px; align-items: flex-start; font-size: 12.5px; line-height: 1.45; cursor: pointer; }
.an-opt input { margin: 2px 0 0; flex: 0 0 auto; }
.an-acts { margin-top: 2px; }

@media (max-width: 520px) {
  /* stacked, or the 104px label column leaves nothing for a long campaign name */
  .an-wrap .kv { flex-direction: column; gap: 2px; }
  .an-wrap .kv > span { flex: 0 0 auto; }
}
