/* Creativ Outreach — the whole stylesheet. There is no second one, and no build step.
 *
 * THE POINT OF THIS FILE (owner, 2026-09-04: "take reference from the chat.creative
 * augmentation.com dashboard to build the outreach dashboard in a better way"). Outreach and
 * Chat Studio are two halves of one product and an operator moves between them all day, so
 * they share a palette, a shell, a card, a table and a button. The palette below is Chat
 * Studio's, token for token — `../instagram-chatbot/studio/design-system/tokens.css` is the
 * generated brand reference and this is a copy of it, not a re-derivation. When the brand
 * moves, that file moves first and this one follows.
 *
 * WHAT IS DELIBERATELY NOT LIFTED
 *   · **The type.** Studio ships 'Outfit' and 'DM Sans' from Google Fonts. Our CSP has no
 *     external stylesheet source at all (security.py), so those faces cannot load, and a
 *     `font-family:'Outfit'` that always falls through is a lie in the source. System stacks,
 *     stated once.
 *   · **The two-column nav rail.** Studio has ten pages and a rail; we have nine and a top
 *     bar, which is what this app already had and what its templates are written against.
 *   · **Anything with a JS dependency** — the account switcher, the notification panel, the
 *     hover-tooltip chart overlay. The whole JS budget here is core.js plus one file per
 *     page (BUILD_PLAN §2).
 *
 * THE THREE-STATE THEME CONTRACT, and every colour in this file obeys it:
 *     bare :root                        → dark, the default
 *     @media (prefers-color-scheme:light) → light, when the OS says so and nothing is stamped
 *     :root[data-theme="light"|"dark"]  → an explicit choice, which outranks both by
 *                                         specificity (0,2,0 over 0,1,0)
 * `static/js/theme-boot.js` stamps the attribute before first paint. It is localStorage, never
 * a cookie — Firebase Hosting strips every cookie but `__session` (CLAUDE.md), so a themed
 * cookie is a preference that works locally and silently dies in production.
 *
 * THE CLASS CONTRACT other pages rely on is at the bottom of this comment so it is read
 * before anything here is renamed. Every one of these already appears in a template:
 *     shell      .shell .side .side-brand .brand-logo .side-nav .side-h .nav-item(.active)
 *                .side-foot .side-user .side-links .theme-btn .main .page-head .foot
 *     surfaces   .card .card h2 .eyebrow .sec-h .grid .grid2 .stats .stat (.lbl .val .sub)
 *                .tile (.n .lbl) .empty
 *     data       table th td .tbl-wrap dl.kv .tl .act .meter
 *     status     .chip (.ok .warn .danger) .badge[data-status] .pill
 *     actions    .btn (.ghost .danger .sm) .rng-switch .rng .quick
 *     forms      .row .row2 .fieldrow .filters .check .addbox .actions label.fl .inp
 *     messages   .flashes .flash (.error .success .warning .info)
 *     standalone .login-* .oauth .bg-glow .narrow .prose
 */

/* ── palette — Chat Studio's tokens, verbatim ─────────────────────────────────── */
:root{
  --accent:#FF5C00; --accent2:#FF8A3D; --accent-soft:rgba(255,92,0,0.12); --accent-text:#FF8A3D;
  --bg:#0a0a0a; --panel:#101010; --text:#F0EDE8; --muted:#888480; --heading:#fff;
  --border:rgba(255,255,255,0.08); --card:rgba(20,20,20,0.72); --card2:rgba(255,255,255,0.03);
  --nav-bg:rgba(10,10,10,0.82); --glow:rgba(255,92,0,0.18);
  --chip:rgba(255,255,255,0.05); --chip-border:rgba(255,255,255,0.10);
  --ok:#41c07a; --ok-bg:rgba(65,192,122,0.13);
  --warn:#e0a13d; --warn-bg:rgba(224,161,61,0.13);
  --danger:#e05a4d; --danger-bg:rgba(224,90,77,0.14);
  --badge-bg:#FF5C00; --badge-fg:#0a0a0a;
}
@media (prefers-color-scheme:light){
  :root{
    --accent-soft:rgba(255,92,0,0.10); --accent-text:#d64f00;
    --bg:#F7F4EF; --panel:#fff; --text:#1a1714; --muted:#6b6560; --heading:#1a1714;
    --border:rgba(0,0,0,0.09); --card:rgba(255,255,255,0.86); --card2:rgba(0,0,0,0.02);
    --nav-bg:rgba(247,244,239,0.88); --glow:rgba(255,92,0,0.10);
    --chip:rgba(0,0,0,0.03); --chip-border:rgba(0,0,0,0.10);
    --ok:#1f9d57; --ok-bg:rgba(31,157,87,0.10);
    --warn:#b3781f; --warn-bg:rgba(179,120,31,0.10);
    --danger:#c4453a; --danger-bg:rgba(196,69,58,0.10);
    --badge-bg:#bf4600; --badge-fg:#fff;
  }
}
/* The explicit choice. Higher specificity than both blocks above, so the toggle wins in
   BOTH directions — a light page on a dark OS and a dark page on a light one. */
:root[data-theme="light"]{
  --accent-soft:rgba(255,92,0,0.10); --accent-text:#d64f00;
  --bg:#F7F4EF; --panel:#fff; --text:#1a1714; --muted:#6b6560; --heading:#1a1714;
  --border:rgba(0,0,0,0.09); --card:rgba(255,255,255,0.86); --card2:rgba(0,0,0,0.02);
  --nav-bg:rgba(247,244,239,0.88); --glow:rgba(255,92,0,0.10);
  --chip:rgba(0,0,0,0.03); --chip-border:rgba(0,0,0,0.10);
  --ok:#1f9d57; --ok-bg:rgba(31,157,87,0.10);
  --warn:#b3781f; --warn-bg:rgba(179,120,31,0.10);
  --danger:#c4453a; --danger-bg:rgba(196,69,58,0.10);
  --badge-bg:#bf4600; --badge-fg:#fff;
}
:root[data-theme="dark"]{
  --accent-soft:rgba(255,92,0,0.12); --accent-text:#FF8A3D;
  --bg:#0a0a0a; --panel:#101010; --text:#F0EDE8; --muted:#888480; --heading:#fff;
  --border:rgba(255,255,255,0.08); --card:rgba(20,20,20,0.72); --card2:rgba(255,255,255,0.03);
  --nav-bg:rgba(10,10,10,0.82); --glow:rgba(255,92,0,0.18);
  --chip:rgba(255,255,255,0.05); --chip-border:rgba(255,255,255,0.10);
  --ok:#41c07a; --ok-bg:rgba(65,192,122,0.13);
  --warn:#e0a13d; --warn-bg:rgba(224,161,61,0.13);
  --danger:#e05a4d; --danger-bg:rgba(224,90,77,0.14);
  --badge-bg:#FF5C00; --badge-fg:#0a0a0a;
}

/* Geometry and type. NOT theme-dependent, so declared once on the bare :root and never
   redefined inside a theme block — the Studio learned that the hard way when its rail width was
   written as a literal in three rules and the rail pinned 11px too high. */
:root{
  --sans:system-ui,-apple-system,'Segoe UI',Roboto,'Helvetica Neue',sans-serif;
  --body:system-ui,-apple-system,'Segoe UI',Roboto,'Helvetica Neue',sans-serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  /* The rail's width. A LAYOUT CONSTANT, named once: the shell's first track and the
     narrow-screen collapse both read it, and the Studio's lesson is that a width written
     as a literal in two places drifts apart the first time one of them is tuned. */
  --side-w:224px;
  --nav-ctl-h:34px;        /* ONE height for every control in the rail */
  --r-card:16px; --r-btn:6px; --r-pill:20px; --r-field:8px;
  --label-sz:10.5px; --label-tr:0.14em;   /* the ROLE A micro-label scale */
}

/* ── reset · body · the page glow ─────────────────────────────────────────────── */
*{box-sizing:border-box;margin:0;padding:0}
body{
  background:var(--bg); color:var(--text); font-family:var(--body);
  font-size:14px; line-height:1.55; -webkit-font-smoothing:antialiased;
  min-height:100vh; display:flex; flex-direction:column; position:relative;
  overflow-x:hidden;
}
/* The soft accent wash behind the top of every page. `position:fixed` and z-index 0, so it
   never enters the flow and never intercepts a click. */
.bg-glow{position:fixed;top:-14%;left:50%;transform:translateX(-50%);width:70vw;height:44vh;
  z-index:0;pointer-events:none;filter:blur(20px);
  background:radial-gradient(ellipse at center,var(--glow),transparent 68%)}

/* ── type ─────────────────────────────────────────────────────────────────────── */
h1,h2,h3,h4{font-family:var(--sans);color:var(--heading);letter-spacing:-0.01em;
  line-height:1.22;text-wrap:balance}
h1{font-size:21px;font-weight:700;margin:0 0 6px}
h2{font-size:15px;font-weight:600;margin:0 0 10px}
h3{font-size:13.5px;font-weight:600;margin:0 0 8px}
p{margin:0 0 10px}
p:last-child{margin-bottom:0}
a{color:var(--accent-text);text-decoration:none}
a:hover{text-decoration:underline}
strong,b{font-weight:600;color:var(--heading)}
code,pre,.mono{font-family:var(--mono);font-size:0.86em}
code{background:var(--card2);border-radius:4px;padding:1px 5px}
pre{overflow-x:auto;background:var(--card2);border:1px solid var(--border);
  border-radius:var(--r-field);padding:12px 14px}
pre code{background:none;padding:0}
hr{border:0;border-top:1px solid var(--border);margin:18px 0}
.muted{color:var(--muted)}
.small{font-size:12px}
.tnum{font-variant-numeric:tabular-nums}

/* ROLE A — the group header. ONE treatment, three names, so no template has to change to
   get it: a section eyebrow, a card's section title, and every table head. Small, tracked,
   uppercase and quiet, because it labels content rather than being content. */
.eyebrow,.sec-h,th{font-family:var(--sans);font-size:var(--label-sz);font-weight:600;
  letter-spacing:var(--label-tr);text-transform:uppercase;color:var(--muted)}
.eyebrow{color:var(--accent-text);display:block;margin:0 0 6px}
.sec-h{margin-bottom:12px}
/* A labelled rule between two halves of a long page. */
.sec-divider{display:flex;align-items:center;gap:14px;margin:28px 0 14px}
.sec-divider::before,.sec-divider::after{content:"";flex:1;height:1px;background:var(--border)}
.sec-divider span{font-family:var(--sans);font-size:var(--label-sz);font-weight:600;
  letter-spacing:var(--label-tr);text-transform:uppercase;color:var(--muted);white-space:nowrap}

/* ── the app shell: the two-column grid and the sticky rail ───────────────────── */
/* A LEFT SIDEBAR, not a top bar (owner, 2026-09-04, revised) — the Studio's `.shell` →
   `.side` + `.main`. The rail is the whole navigation AND the identity block: the Studio
   splits those between a header and a rail because its header also carries per-page
   actions, and we have no such header.

   `min-width:0` on the second track is load-bearing. A grid item's automatic minimum is
   MIN-CONTENT, so one un-breakable cell in a wide table would pin the `1fr` track open and
   push the page off the right edge instead of scrolling inside its own card. Chat Studio
   paid for this three times (its §CP2/§CP3/§CP4). */
.shell{display:grid;grid-template-columns:var(--side-w) minmax(0,1fr);min-height:100vh;
  position:relative;z-index:1}

/* Sticky and self-scrolling: the rail stays put while the page moves, and a nav taller than
   a short laptop screen scrolls INSIDE the rail rather than being cut off. `align-self:start`
   stops the grid item stretching to the row height, which is what makes `position:sticky`
   mean anything here. */
.side{display:flex;flex-direction:column;gap:2px;padding:18px 12px;
  border-right:1px solid var(--border);background:var(--nav-bg);backdrop-filter:blur(16px);
  position:sticky;top:0;align-self:start;height:100vh;overflow-y:auto}

.side-brand{display:flex;align-items:center;gap:10px;padding:4px 10px 18px;
  white-space:nowrap;font-family:var(--sans);font-weight:700;font-size:14px;
  color:var(--heading);letter-spacing:-0.01em}
.side-brand:hover{text-decoration:none}
.side-brand span{color:var(--accent)}
/* The logo is white-on-transparent, so it is INVERTED whenever the page is light and left
   alone in dark — stated for both light states, never for the default. */
.brand-logo{height:24px;width:auto;display:block;flex:0 0 auto}
:root[data-theme="light"] .brand-logo{filter:invert(1)}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]) .brand-logo{filter:invert(1)}}

.side-nav{display:flex;flex-direction:column;gap:2px}
/* ROLE A type comes from the `.eyebrow,.sec-h,th` rule; this is only its spacing. */
.side-h{padding:6px 12px 6px}
.nav-item + .side-h{margin-top:14px}
.nav-item{display:flex;align-items:center;gap:11px;padding:9px 12px;border-radius:8px;
  font-family:var(--sans);font-size:13px;font-weight:500;color:var(--muted);
  border:1px solid transparent;transition:.15s;text-decoration:none}
.nav-item svg{width:16px;height:16px;flex:0 0 16px;stroke:currentColor;fill:none;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.nav-item:hover{color:var(--text);background:var(--card2);text-decoration:none}
/* `.active` is the ONLY thing that colours a nav item. No item carries permanent emphasis:
   the Studio shipped a bold "core page" item twice and both times the owner read the weight
   as "this one is selected" — emphasis that can be mistaken for state costs you the state
   signal on every other page. */
.nav-item.active{color:var(--heading);background:var(--accent-soft);
  border-color:rgba(255,92,0,0.22)}
.nav-item.active svg{stroke:var(--accent)}

/* The identity block, pinned to the foot of the rail. `margin-top:auto` is what pins it;
   the top border is the divider between "where you can go" and "who you are". */
.side-foot{margin-top:auto;padding:12px 4px 4px;border-top:1px solid var(--border);
  display:flex;flex-direction:column;gap:8px}
.side-user{display:flex;align-items:center;gap:8px;min-width:0}
.side-user .email{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;font-family:var(--sans);font-size:11.5px;color:var(--muted)}
.side-foot .btn{width:100%;justify-content:center}
.side-links{font-size:11px;color:var(--muted);padding:0 2px}
.side-links a{color:var(--muted)}
.side-links a:hover{color:var(--accent-text)}
.theme-btn{width:var(--nav-ctl-h);height:var(--nav-ctl-h);flex:0 0 auto;
  border-radius:var(--r-field);border:1px solid var(--chip-border);background:var(--chip);
  color:var(--text);cursor:pointer;display:flex;align-items:center;justify-content:center;
  font-size:15px;line-height:1;transition:.18s}
.theme-btn:hover{border-color:var(--accent)}

/* ── the page column and the footer ───────────────────────────────────────────── */
.main{min-width:0;padding:26px 30px 60px}
.page-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;
  flex-wrap:wrap;margin-bottom:18px}
.page-head .lead{max-width:62ch}
.page-head .acts{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.foot{margin-top:36px;padding-top:16px;border-top:1px solid var(--border);
  font-size:11.5px;color:var(--muted)}
.foot a{color:var(--muted)}
.foot a:hover{color:var(--accent-text)}

/* ── cards and grids ──────────────────────────────────────────────────────────── */
.card{background:var(--card);border:1px solid var(--border);border-radius:var(--r-card);
  padding:18px 20px;margin:0 0 16px;backdrop-filter:blur(8px);min-width:0}
.card:last-child{margin-bottom:0}
.card h2{font-family:var(--sans);font-size:14px;font-weight:600;color:var(--heading);
  margin:0 0 12px}
.card h2 .badge,.card h2 .chip{margin-left:8px;vertical-align:middle}
/* A card whose whole body is a table wants the table flush to its edges. */
.card.flush{padding:0;overflow:hidden}
.card.flush > .card-h{padding:16px 20px 12px}
.card-h{display:flex;align-items:center;justify-content:space-between;gap:12px;
  flex-wrap:wrap;margin-bottom:12px}
.card-h h2{margin:0}

.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}
/* A card in a grid gets its spacing from `gap`. Leaving `.card`'s own margin-bottom on it
   adds a second, different gap that only shows once the grid WRAPS — so the tiles look
   evenly spaced on a wide screen and ragged on a laptop, which is the hardest kind of
   spacing bug to notice on the machine it was built on. */
.grid > .card,.stats > .card{margin-bottom:0}
/* `min-width:0` on the children is not decoration: a grid item's automatic minimum is
   min-content, so one un-breakable cell (a long subject line, an email address) pins the
   track open and the next column is pushed off the right edge. Chat Studio has paid for
   this exact bug three times; one declaration prevents it here. */
.grid > *,.grid2 > *{min-width:0}
.grid2{display:grid;grid-template-columns:1.5fr 1fr;gap:16px;align-items:start}

/* ── stat tiles ───────────────────────────────────────────────────────────────── */
/* `.stat` is the Studio's KPI tile; `.tile` (.n/.lbl) is the older spelling this app's
   templates already use. They are the same object with the label above or below the number,
   and both are kept so no page has to be rewritten to look right. */
.stats{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));
  margin-bottom:18px}
.stats > *{min-width:0}
.stat{padding:14px 16px}
/* PROMOTED out of the micro-label system: on this page "Replied" IS the content, not a
   caption for it. Sentence case, --text, and large enough to read as a thing rather than
   as an annotation. */
.stat .lbl{font-family:var(--sans);font-size:12px;font-weight:600;letter-spacing:0;
  text-transform:none;color:var(--text)}
.stat .val{font-family:var(--sans);font-size:26px;font-weight:800;letter-spacing:-0.02em;
  color:var(--heading);margin-top:6px;line-height:1;font-variant-numeric:tabular-nums}
.stat .val small{font-size:14px;font-weight:600;color:var(--muted)}
.stat .sub{font-size:11px;color:var(--muted);margin-top:6px}
.stat .sub.ok{color:var(--ok)}
.stat .sub.warn{color:var(--warn)}
.stat .sub.bad{color:var(--danger)}
/* The one tile that must be noticed before the others. */
.stat.alert{border-color:var(--danger)}
.stat.alert .val{color:var(--danger)}

.tile{padding:14px 16px}
.tile .n{font-family:var(--sans);font-size:26px;font-weight:800;letter-spacing:-0.02em;
  color:var(--heading);line-height:1.05;font-variant-numeric:tabular-nums}
.tile .lbl{font-family:var(--sans);font-size:12px;font-weight:600;color:var(--muted);
  margin-top:6px}

/* ── tables ───────────────────────────────────────────────────────────────────── */
/* `.tbl-wrap` is the scroll container: a table with eight columns must scroll inside its
   own box, never take the document with it. A bare <table> outside one still renders — it
   simply gets no min-width, so it compresses instead of overflowing. */
/* `max-height` + `overflow:auto` is what makes the sticky header REAL rather than declared.
   A sticky `th` sticks to its nearest scrolling ancestor, and a wrapper that only scrolls
   sideways never scrolls past its own header — the rule would be inert. 78vh costs a short
   table nothing (it never reaches the ceiling, so no scrollbar appears and nothing moves)
   and gives a 200-row contact list a header that stays with the rows. */
.tbl-wrap{overflow:auto;max-height:78vh;border:1px solid var(--border);border-radius:14px;
  background:var(--card);max-width:100%}
.card .tbl-wrap{border:0;border-radius:0;background:none;margin:0 -20px -18px;
  padding:0 20px 4px}
table{width:100%;border-collapse:collapse}
.tbl-wrap > table{min-width:640px}
/* OPAQUE, deliberately: `--card` is translucent, and a see-through sticky header with rows
   sliding under it is worse than no sticky header at all. */
th{text-align:left;padding:11px 14px;border-bottom:1px solid var(--border);white-space:nowrap;
  position:sticky;top:0;z-index:1;background:var(--panel)}
td{padding:11px 14px;font-size:12.5px;border-bottom:1px solid var(--border);
  vertical-align:top;color:var(--text)}
tbody tr:last-child td,table tr:last-child td{border-bottom:0}
/* Zebra first, hover second — source order is what lets the pointer win on an even row. */
tbody tr:nth-child(even){background:var(--card2)}
tbody tr:hover{background:var(--chip)}
td .who,td .nm{font-family:var(--sans);font-weight:600;color:var(--heading);display:block}
td .sub{font-size:11px;color:var(--muted)}
td.n,th.n{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
td code{white-space:nowrap}
/* A compact variant for a dense card — the sender table, the day breakdown. */
.tbl-sm th,.tbl-sm td{padding:8px 10px;font-size:12px}

dl.kv{display:grid;grid-template-columns:minmax(9rem,auto) 1fr;gap:6px 18px;margin:0}
dl.kv dt{font-family:var(--sans);font-size:12px;font-weight:500;color:var(--muted)}
dl.kv dd{margin:0;font-size:12.5px;color:var(--text);min-width:0;overflow-wrap:anywhere}

/* The contact timeline: one event per row, no bullets. */
ul.tl{list-style:none;margin:0;padding:0}
ul.tl li{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;
  padding:9px 0;border-bottom:1px solid var(--border)}
ul.tl li:last-child{border-bottom:0}

/* The dashboard's activity feed — the same row shape, with the time at the right. */
.act{display:flex;align-items:center;gap:12px;padding:10px 0;
  border-bottom:1px solid var(--border)}
.act:last-child{border-bottom:0}
.act .txt{flex:1 1 auto;min-width:0}
.act .txt .q{font-size:12.5px;color:var(--text);overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
.act .txt .m{font-size:11px;color:var(--muted);margin-top:2px}
.act .t{flex:0 0 auto;font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums}

/* ── status vocabulary: chips, badges, pills ──────────────────────────────────── */
/* `.chip` is what every existing page already writes, with `.ok`/`.warn`/`.danger`. Kept
   exactly as it was so nothing has to be edited to keep working. */
.chip{display:inline-flex;align-items:center;gap:5px;font-family:var(--sans);font-size:11px;
  font-weight:600;padding:2px 9px;border-radius:var(--r-btn);background:var(--chip);
  border:1px solid var(--chip-border);color:var(--muted);white-space:nowrap}
.chip.ok{background:var(--ok-bg);border-color:transparent;color:var(--ok)}
.chip.warn{background:var(--warn-bg);border-color:transparent;color:var(--warn)}
.chip.danger{background:var(--danger-bg);border-color:transparent;color:var(--danger)}
.chip.accent{background:var(--accent-soft);border-color:transparent;color:var(--accent-text)}

/* `.badge[data-status]` is the SAME vocabulary written once, keyed on the value the data
   layer already stores — `data.senders.STATUSES`, `data.campaigns.STATUSES`,
   `data.enrollments.STATUSES` and `data.events.TYPES`. A template writes
   `<span class="badge" data-status="{{ s.status }}">` and the colour is decided here rather
   than by a Jinja conditional repeated on nine pages, which is how `needs_reauth` ends up
   amber on one page and red on another. Anything unlisted falls through to the quiet
   default, which is the right answer for a status this file has not met yet. */
.badge{display:inline-flex;align-items:center;gap:5px;font-family:var(--sans);font-size:11px;
  font-weight:600;letter-spacing:0.02em;padding:2px 9px;border-radius:var(--r-pill);
  background:var(--chip);border:1px solid var(--chip-border);color:var(--muted);
  white-space:nowrap;text-transform:capitalize}
.badge .dot{width:6px;height:6px;border-radius:50%;background:currentColor;flex:0 0 6px}
.badge[data-status="connected"],.badge[data-status="running"],.badge[data-status="active"],
.badge[data-status="delivered"],.badge[data-status="replied"],.badge[data-status="sent"],
.badge[data-status="completed"],.badge[data-status="opened"],.badge[data-status="read"]{
  background:var(--ok-bg);border-color:transparent;color:var(--ok)}
.badge[data-status="paused"],.badge[data-status="scheduled"],.badge[data-status="over_cap"],
.badge[data-status="exited"],.badge[data-status="auto_reply"],.badge[data-status="unsubscribed"]{
  background:var(--warn-bg);border-color:transparent;color:var(--warn)}
.badge[data-status="needs_reauth"],.badge[data-status="failed"],.badge[data-status="bounced"],
.badge[data-status="stop"]{
  background:var(--danger-bg);border-color:transparent;color:var(--danger)}
.badge[data-status="clicked"],.badge[data-status="enrolled"]{
  background:var(--accent-soft);border-color:transparent;color:var(--accent-text)}
/* draft and archived stay on the quiet default on purpose: they are the two statuses that
   mean "nothing is happening", and a colour would claim otherwise. */

.pill{display:inline-flex;align-items:center;gap:6px;font-family:var(--sans);font-size:11px;
  font-weight:600;letter-spacing:0.03em;padding:4px 11px;border-radius:var(--r-pill);
  background:var(--chip);color:var(--muted)}
.pill.ok{background:var(--ok-bg);color:var(--ok)}
.pill.warn{background:var(--warn-bg);color:var(--warn)}
.pill.danger{background:var(--danger-bg);color:var(--danger)}
.pill .dot{width:6px;height:6px;border-radius:50%;background:currentColor;
  box-shadow:0 0 0 3px currentColor;opacity:.999}
/* The channel glyph in front of an identity — one character, held to a square so the email
   and WhatsApp rows line up. */
.glyph{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;
  flex:0 0 22px;border-radius:var(--r-btn);background:var(--chip);font-size:12px;
  line-height:1}

/* ── buttons ──────────────────────────────────────────────────────────────────── */
/* `.btn` IS THE PRIMARY here, unlike the Studio's outline default — every existing template
   writes `<a class="btn">` for the page's main action ("New campaign", "Add contact",
   "Connect a Google mailbox") and `.btn.ghost` for the quiet one. Inverting that to match
   the Studio would silently demote the primary action on nine pages. Same three tiers, one
   name shifted. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;
  font-family:var(--sans);font-size:12.5px;font-weight:600;line-height:1.2;
  padding:9px 16px;border-radius:var(--r-btn);cursor:pointer;text-decoration:none;
  border:1px solid var(--accent);background:var(--accent);color:#fff;
  box-shadow:0 6px 18px -8px var(--glow);transition:.18s;white-space:nowrap}
.btn:hover{background:var(--accent2);border-color:var(--accent2);color:#fff;
  text-decoration:none}
.btn.ghost{background:transparent;border-color:var(--chip-border);color:var(--text);
  box-shadow:none}
.btn.ghost:hover{background:var(--chip);border-color:var(--accent);color:var(--text)}
.btn.quiet{background:transparent;border-color:transparent;color:var(--accent-text);
  box-shadow:none;padding-left:6px;padding-right:6px}
.btn.quiet:hover{background:transparent;color:var(--accent)}
.btn.danger{background:transparent;border-color:var(--danger);color:var(--danger);
  box-shadow:none}
.btn.danger:hover{background:var(--danger-bg);border-color:var(--danger);color:var(--danger)}
.btn.sm{padding:6px 11px;font-size:11.5px}
.btn:disabled,.btn.is-off{opacity:.5;cursor:default;box-shadow:none}
/* `.btn` sets display:inline-flex, which OUTRANKS the UA's `[hidden]{display:none}` — an
   author rule of any specificity does. Without this a hidden button never disappears. */
.btn[hidden]{display:none}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* A row of shortcuts. Used on the dashboard; available to any page that grows one. */
.quick{display:flex;gap:10px;flex-wrap:wrap;align-items:center}

/* ── forms ────────────────────────────────────────────────────────────────────── */
/* The existing pages write BARE `<label>` + `<input>`/`<select>` inside a `<div>` inside
   `.row` / `.row2` / `.fieldrow` / `.filters`. Those bare elements are styled here, so a
   page picks up the design system without a single markup change; `label.fl` and `.inp` are
   the named classes for anything written from now on, and resolve to the same thing. */
label,label.fl{font-family:var(--sans);font-size:12px;font-weight:500;letter-spacing:0;
  text-transform:none;color:var(--text);display:block;margin:0 0 6px}
input[type=text],input[type=email],input[type=number],input[type=search],input[type=url],
input[type=tel],input[type=password],input[type=date],input[type=time],input[type=file],
select,textarea,.inp{
  width:100%;background:var(--bg);border:1px solid var(--border);border-radius:var(--r-field);
  padding:9px 11px;color:var(--text);font-family:var(--body);font-size:13px;resize:vertical}
select{appearance:none;-webkit-appearance:none;cursor:pointer;padding-right:30px;
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
                   linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:right 14px top 50%,right 9px top 50%;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat}
input:focus,select:focus,textarea:focus,.inp:focus{outline:none;border-color:var(--accent)}
textarea{min-height:120px;line-height:1.5;font-family:var(--mono);font-size:12.5px}
input[type=checkbox],input[type=radio]{width:auto;accent-color:var(--accent);cursor:pointer}
input[type=file]{padding:8px 10px;font-size:12px;cursor:pointer}

/* Field rows. `.row` is a free-flowing set of fields, `.row2` a pair, `.fieldrow` a
   four-across block, `.filters` the query bar above a list. auto-fit everywhere, so each
   collapses to one column on a phone with no media query of its own. */
.row,.fieldrow{display:grid;gap:12px 14px;margin-bottom:12px;
  grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.row2{display:grid;gap:12px 14px;margin-bottom:12px;
  grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
.row > *,.row2 > *,.fieldrow > *,.filters > *{min-width:0}
.filters{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;margin-bottom:14px;
  padding:14px 16px;background:var(--card);border:1px solid var(--border);
  border-radius:var(--r-card)}
.filters > div{flex:1 1 150px;min-width:0}
.filters .btn{flex:0 0 auto}
/* A checkbox and its words on ONE line — the label rule above is `display:block`, which
   would otherwise stack them. */
.check,label.check{display:inline-flex;align-items:center;gap:8px;margin:0 14px 6px 0;
  font-size:12.5px;color:var(--text);cursor:pointer}
.check input{margin:0}
/* One step of a campaign sequence: a card inside a card, so the nesting is visible. */
.step{border:1px solid var(--border);border-radius:12px;background:var(--card2);
  padding:14px 16px;margin:0 0 12px}
.step h3{font-size:12.5px;margin-bottom:10px}
/* A form folded behind a summary line. */
.addbox{border:1px solid var(--border);border-radius:12px;background:var(--card2);
  padding:12px 14px}
.addbox > summary{font-family:var(--sans);font-size:12.5px;font-weight:600;
  color:var(--accent-text);cursor:pointer;list-style:none}
.addbox > summary::-webkit-details-marker{display:none}
.addbox > summary::before{content:"▸ ";color:var(--muted)}
.addbox[open] > summary::before{content:"▾ "}
/* A row of buttons at the end of a form or a detail card. */
.actions{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-top:14px}
.hint{font-size:11.5px;color:var(--muted);margin-top:6px}

/* ── the CSV import's two wide tables ─────────────────────────────────────────── */
/* These two hooks predate this stylesheet and both name a REAL layout problem, so they get
   rules rather than being left as decoration: a mapping table is a form laid out as a grid,
   and a sample table is as wide as whatever CSV was uploaded. `.sup`, `.up` and `.f` are the
   other three inherited hooks; they are plain `.card` / `<form>` modifiers with nothing
   left to say once the base rules exist, and inventing a look for them would be three
   arbitrary decisions nothing asked for. */
.maptable td{vertical-align:middle;white-space:normal}
.maptable td:first-child{font-family:var(--mono);font-size:12px}
.maptable select{min-width:9rem}
/* A preview of someone else's spreadsheet: every column is unknown and there may be forty
   of them, so the cells truncate and the table scrolls inside its wrapper rather than
   deciding how wide the page is. */
.samples table{table-layout:fixed}
.samples td,.samples th{max-width:14rem;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}

/* ── messages ─────────────────────────────────────────────────────────────────── */
.flashes{list-style:none;margin:0 0 16px;padding:0;display:grid;gap:8px}
.flash{padding:10px 14px;border-radius:var(--r-field);font-size:12.5px;
  background:var(--card2);border:1px solid var(--border);color:var(--text)}
.flash.info{background:var(--chip);border-color:var(--chip-border)}
.flash.success{background:var(--ok-bg);border-color:transparent;color:var(--ok)}
.flash.warning{background:var(--warn-bg);border-color:transparent;color:var(--warn)}
.flash.error{background:var(--danger-bg);border-color:transparent;color:var(--danger)}
.flash a{color:inherit;text-decoration:underline}
/* A degraded-read banner is a flash that is not a message ABOUT something the operator
   just did, so it sits inline in the page and keeps its own margin. */
.banner{display:block;margin:0 0 16px}

/* ── empty states ─────────────────────────────────────────────────────────────── */
/* An empty table is a question ("is it broken, or is there nothing?"), so every list says
   which. `.big` is the quiet glyph above the sentence. */
.empty{padding:34px 20px;text-align:center;color:var(--muted);font-size:12.5px}
.empty .big{font-size:24px;margin-bottom:8px;opacity:.5;display:block}
.empty p{margin:0 0 10px}
.empty .btn{margin-top:4px}

/* ── the range switcher ───────────────────────────────────────────────────────── */
/* Real <a href="?range=…"> links inside a segmented control, so it works with JS off; the
   server decides which one is active. core.js only REMEMBERS the choice. */
.rng-switch{display:inline-flex;gap:2px;padding:3px;border:1px solid var(--border);
  border-radius:var(--r-field);background:var(--card2)}
.rng{font-family:var(--sans);font-size:12px;font-weight:600;color:var(--muted);
  padding:6px 12px;border-radius:var(--r-btn);text-decoration:none;line-height:1.1;
  transition:background .12s,color .12s;white-space:nowrap}
.rng:hover{color:var(--text);text-decoration:none}
.rng.active{background:var(--accent-soft);color:var(--accent-text)}

/* ── the sends-per-day chart ──────────────────────────────────────────────────── */
/* The SVG is stretched to the card by `preserveAspectRatio="none"`, which distorts nothing:
   every mark page-dashboard.js draws is an axis-aligned rectangle or a horizontal rule. The
   day labels are DOM under the plot rather than <text> in that space, so they keep their
   real size at every card width. */
.dash-chart{padding:16px 20px}
.dc-head{display:flex;align-items:center;justify-content:space-between;gap:12px;
  flex-wrap:wrap;margin-bottom:10px}
.dc-title{font-family:var(--sans);font-size:12.5px;font-weight:600;color:var(--muted)}
.dc-key{display:flex;align-items:center;gap:14px;font-size:11px;color:var(--muted)}
.dc-key i{display:inline-block;width:10px;height:10px;border-radius:3px;
  background:var(--accent);margin-right:5px;vertical-align:-1px;font-style:normal}
.dc-key i.cap{background:none;border-top:2px dashed var(--warn);height:0;border-radius:0;
  vertical-align:3px}
.dc-svg{width:100%;height:180px;display:block}
.dc-axis{display:flex;margin-top:6px;font-family:var(--sans);font-size:10.5px;
  color:var(--muted)}
.dc-tick{flex:1 1 0;min-width:0;text-align:center;overflow:hidden;white-space:nowrap}

/* ── the cap meter ────────────────────────────────────────────────────────────── */
/* `sent today / daily cap` as a bar. The fill turns amber near the ceiling and red at it,
   because "47/50" and "50/50" are one character apart and mean completely different things
   to whoever is watching the mailbox. */
/* `display:block` is load-bearing: the templates write this as a <span>, and height and
   min-width do nothing at all on an inline box — the bar would render as zero pixels of
   nothing, in every browser, with every declaration below it present and correct. */
.meter{display:block;height:6px;border-radius:6px;background:var(--chip);overflow:hidden;
  min-width:90px;max-width:140px;margin-top:5px}
.meter i{display:block;height:100%;border-radius:6px;background:var(--accent);
  font-style:normal}
.meter.near i{background:var(--warn)}
.meter.full i{background:var(--danger)}
.meter-n{font-family:var(--sans);font-size:12px;font-weight:600;color:var(--heading);
  font-variant-numeric:tabular-nums}

/* ── standalone pages: login, privacy, the error page ─────────────────────────── */
/* Sign-in does not extend base.html — there is no session, so a nav would be a wall of
   links that all bounce back here. The look is Chat Studio's login: the logo, the soft
   glow, the centred card, the full-width provider button. What is NOT lifted is the Google
   Identity Services script — our CSP forbids it, and sign-in here is a server-side redirect
   (auth.py), so the button is a plain link wearing the same clothes. */
.login-body{display:block;min-height:100vh}
/* The sign-in page has no shell to hang the toggle in, so it floats. Same button, same id,
   same core.js listener — the class is unchanged and only its position is stated here. */
.login-body .theme-btn{position:fixed;top:20px;right:20px;z-index:2}
.login-wrap{position:relative;z-index:1;min-height:100vh;display:flex;align-items:center;
  justify-content:center;padding:24px}
.login-card{width:100%;max-width:400px;text-align:center;padding:40px 34px;
  background:var(--card);border:1px solid var(--border);border-radius:var(--r-card);
  backdrop-filter:blur(8px)}
.login-card .lg{height:30px;margin:0 auto 26px;display:block}
:root[data-theme="light"] .login-card .lg{filter:invert(1)}
@media (prefers-color-scheme:light){:root:not([data-theme="dark"]) .login-card .lg{filter:invert(1)}}
.login-card h2{font-size:21px;font-weight:700;margin-bottom:6px}
.login-card p{font-size:13px;color:var(--muted);margin-bottom:26px}
.login-card .btn{width:100%;margin:.4rem 0 0}
.oauth{display:flex;align-items:center;justify-content:center;gap:11px;width:100%;height:46px;
  border-radius:9px;border:1px solid var(--chip-border);background:var(--chip);
  color:var(--text);font-family:var(--sans);font-size:13.5px;font-weight:600;cursor:pointer;
  margin-bottom:12px;transition:.18s;text-decoration:none}
.oauth:hover{border-color:var(--accent);text-decoration:none;color:var(--text)}
.login-err{color:var(--danger);font-size:12.5px;margin:0 0 14px;min-height:18px}
.login-foot{margin-top:22px;font-size:11.5px;color:var(--muted)}
.login-links{margin-top:10px;font-size:11.5px;color:var(--muted)}
.login-links a{color:var(--muted)}

/* Privacy, Terms, the unsubscribe pages and the error page: one narrow column. */
.narrow{position:relative;z-index:1;max-width:38rem;margin:3rem auto;padding:0 22px}
.narrow.wide{max-width:50rem}
.prose{font-size:13.5px;line-height:1.65}
.prose h2{margin-top:26px;font-size:15px}
.prose h3{margin-top:18px}
.prose ul,.prose ol{padding-left:1.2rem;margin:0 0 12px}
.prose li{margin:.25rem 0}

/* ── responsive · reduced motion ──────────────────────────────────────────────── */
/* THE NARROW-SCREEN COLLAPSE, mirroring the Studio's: below 940px the two-column grid
   becomes one column and the rail becomes a HORIZONTAL WRAPPING BAR across the top —
   `flex-direction:row` + `flex-wrap`, its right border traded for a bottom one, and
   `position:static` so it scrolls away with the page instead of eating the viewport.
   The Studio hides its group headers and rail footer at this width for the same reason we
   do: stacked labels in a wrapping row read as more items, not as structure.

   NO JAVASCRIPT, and that is the point — no menu button to fail, nothing to remember, and
   it works identically with JS off. The identity block stays visible (the Studio can hide
   its rail footer because its header carries identity; ours does not exist), so it simply
   loses its `margin-top:auto` and joins the row. */
@media (max-width:940px){
  .shell{grid-template-columns:1fr}
  .side{flex-direction:row;flex-wrap:wrap;align-items:center;gap:4px;
    border-right:0;border-bottom:1px solid var(--border);padding:10px 14px;
    position:static;height:auto;overflow:visible}
  .side-brand{padding:0 12px 0 0}
  .side-nav{flex-direction:row;flex-wrap:wrap;flex:1 1 auto;gap:3px}
  .side-h{display:none}
  .nav-item + .side-h{margin-top:0}
  .nav-item{padding:7px 10px;font-size:12.5px}
  .side-foot{margin-top:0;border-top:0;padding:0;flex-direction:row;align-items:center;
    gap:8px;width:auto}
  .side-user .email{max-width:18ch;flex:0 1 auto}
  .side-foot .btn{width:auto}
  .side-links{display:none}
  .main{padding:18px 16px 48px}
  .grid2{grid-template-columns:1fr}
  .card{padding:15px 16px}
  .card .tbl-wrap{margin:0 -16px -15px;padding:0 16px 4px}
}
@media (max-width:560px){
  .side-brand span{display:none}     /* the logo carries the identity on a phone */
  .side-user .email{display:none}    /* one operator per browser; the Sign out is the point */
  .stats{grid-template-columns:repeat(auto-fit,minmax(132px,1fr))}
  .main{padding:16px 12px 40px}
}
@media (prefers-reduced-motion:reduce){
  *{animation:none!important;transition:none!important;scroll-behavior:auto!important}
}
@media print{
  .side,.foot,.quick,.rng-switch,.theme-btn,.bg-glow{display:none!important}
  .shell{grid-template-columns:1fr}
  .card{break-inside:avoid;border-color:#ccc}
}

/* S-07: the nav sign-out is a one-button POST form; it must sit inline like the link it replaced. */
form.inline{display:inline;margin:0}
