/* ==========================================================================
   Tawak admin console — shared styles for the Dashboard and Push Console views.
   Ported from push/index.html (the original console) plus the dashboard.
   ========================================================================== */

:root {
  --text: #ffffff;
  --muted: rgba(255, 255, 255, 0.55);
  --faint: rgba(255, 255, 255, 0.35);
  --green: #59ed97;
  --amber: #ffcb45;
  --red: #ff6b63;
  --blue: #4f7cff;
  --panel: rgba(255, 255, 255, 0.07);
  --panel-lift: rgba(255, 255, 255, 0.105);
  --panel-border: rgba(255, 255, 255, 0.12);
  --title: rgba(255, 255, 255, 0.92);
}

* { box-sizing: border-box; }

/* The dark base is set on BOTH html and body, otherwise the html element shows
   through as white bars above/below the gradient, and on overscroll. */
html, body {
  height: 100%;
  font-family: 'Inter', sans-serif;
  color: var(--text);
  background: #00131f;
  /* `clip`, not `hidden`: `hidden` turns these into scroll containers, which
     silently breaks `position: sticky` on descendants (the range filter). */
  overflow-x: clip;
}
body {
  margin: 0;
  min-height: 100vh;
  background: radial-gradient(ellipse 80% 80% at 70% 50%, #0e3d35 0%, #081e26 50%, #00131f 100%);
  -webkit-font-smoothing: antialiased;
}

#gate { display: none; }
#app { display: none; }
.hidden { display: none !important; }

/* ----- Nav bar ----- */
nav {
  position: fixed; top: 20px; left: 50%; transform: translateX(-50%);
  width: calc(100% - 40px); max-width: 1200px; z-index: 1000;
  background: rgba(0, 19, 31, 0.4);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  border-radius: 16px; padding: 14px 28px;
  display: flex; justify-content: space-between; align-items: center;
  transition: all 0.3s ease;
}
.logo { display: flex; align-items: center; text-decoration: none; }
.logo svg { height: 26px; width: auto; }
.nav-links { display: flex; gap: 22px; align-items: center; }
.nav-link {
  color: var(--muted); text-decoration: none; font-size: 15px;
  font-weight: 500; transition: color 0.15s ease;
}
.nav-link:hover { color: #ffffff; }
.nav-btn {
  color: var(--muted) !important; border: 1px solid rgba(255, 255, 255, 0.15);
  padding: 8px 16px; border-radius: 8px; transition: all 0.15s ease;
}
.nav-btn:hover {
  border-color: var(--green); color: var(--green) !important;
  background: rgba(89, 237, 151, 0.05);
}
/* Sign out is a real <button>, so reset the browser defaults explicitly. */
button.nav-btn {
  font-family: inherit; font-size: 15px; font-weight: 500; cursor: pointer;
  background: transparent; color: var(--green) !important;
  border: 1px solid rgba(89, 237, 151, 0.45);
}
button.nav-btn:hover { background: rgba(89, 237, 151, 0.12); border-color: var(--green); color: var(--green) !important; }
button.nav-btn:active { background: rgba(89, 237, 151, 0.2); }

.nav-toggle {
  display: none; background: transparent; border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 8px; padding: 7px 9px; color: var(--text); cursor: pointer;
  align-items: center; transition: all 0.15s ease;
}
.nav-toggle:hover { border-color: var(--green); color: var(--green); }

.nav-sheet {
  position: fixed; top: 74px; left: 50%; transform: translateX(-50%);
  width: calc(100% - 40px); max-width: 1200px; z-index: 999;
  background: rgba(0, 19, 31, 0.92);
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  border-radius: 14px; padding: 14px; display: flex; flex-direction: column; gap: 12px;
  animation: sheetIn 0.18s ease;
}
.nav-sheet[hidden] { display: none; }
@keyframes sheetIn {
  from { opacity: 0; transform: translateX(-50%) translateY(-6px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
.nav-sheet-row { display: flex; flex-wrap: wrap; gap: 8px; }
.nav-sheet .chip { font-size: 12px; }
.nav-sheet button.nav-btn, .nav-sheet a.nav-btn { width: 100%; text-align: center; padding: 11px; }

@media (max-width: 900px) {
  .nav-links { display: none; }
  .nav-toggle { display: inline-flex; }
}

/* ----- Chips + dots ----- */
.chip {
  display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px;
  padding: 5px 12px; border-radius: 999px; color: var(--muted);
  background: rgba(255, 255, 255, 0.05); white-space: nowrap;
}
.chip b { color: #fff; font-weight: 600; }
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--faint); flex: 0 0 auto; display: inline-block; }
.dot.ok { background: var(--green); box-shadow: 0 0 8px rgba(89, 237, 151, 0.7); }
.dot.warn { background: var(--amber); }
.dot.err { background: var(--red); }
.dot.off { background: rgba(255, 255, 255, 0.25); }

/* ----- Sign-in gate: dark artwork beside a floating glass panel ----- */
.gate-shell { min-height: 100vh; display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); }
.gate-art {
  position: relative; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background: radial-gradient(ellipse 80% 80% at 70% 50%, #0e3d35 0%, #081e26 50%, #00131f 100%);
}
/* The signature glow sits deliberately off-centre, so the logo is the only
   centred element in the artwork area. Kept as a soft wash rather than a bright
   spot so it reads as depth behind the logo. */
.gate-art::before {
  content: ''; position: absolute; left: 58%; top: 32%; width: 92%; aspect-ratio: 1;
  transform: translate(-50%, -50%); pointer-events: none;
  background: radial-gradient(circle at center, rgba(33, 80, 172, 0.42) 0%, rgba(33, 80, 172, 0) 68%);
}
.gate-logo-link { position: relative; display: block; }
.gate-logo { display: block; width: min(340px, 34vw); height: auto; }

/* Floating panel — gaps at the top, bottom and right only, nothing on the left
   so it sits against the artwork. */
.gate-panel-col { display: flex; padding: 44px 44px 44px 0; }
.gate-panel {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center;
  padding: 40px 34px; border-radius: 20px;
  background: rgba(255, 255, 255, 0.04);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
}
.gate-panel h2 { margin: 0 0 10px; font-size: 25px; font-weight: 700; letter-spacing: -0.3px; }
.gate-panel .gate-sub { margin: 0 0 26px; max-width: 330px; font-size: 14px; line-height: 1.6; color: rgba(255, 255, 255, 0.75); }
.gate-note { font-size: 12px; color: var(--faint); margin: 20px 0 0; line-height: 1.5; max-width: 330px; }

@media (max-width: 900px) {
  /* Phone: the artwork and panel are centred as one group, so the panel sits in
     the middle of the screen with the logo above it. */
  .gate-shell {
    display: flex; flex-direction: column; justify-content: center;
    /* The artwork's glow is clipped by its own short box on phones, which drew a
       hard horizontal edge, so the glow lives on the full-height shell instead. */
    background: radial-gradient(ellipse 100% 30% at 50% 44%, rgba(33, 80, 172, 0.3) 0%, rgba(33, 80, 172, 0) 72%);
  }
  .gate-art { background: none; min-height: 0; padding: 0 20px 26px; }
  .gate-art::before { content: none; }
  .gate-logo { width: min(200px, 46vw); }
  .gate-panel-col { padding: 0 22px; }
  /* width:100% so it fills the column; flex:0 0 auto so it keeps its content
     height instead of stretching. */
  .gate-panel { flex: 0 0 auto; width: 100%; padding: 32px 22px; border-radius: 24px; }
  .gate-panel h2 { font-size: 22px; }
}

/* Google's official "Sign in with Google" button branding. */
.gsi-button {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  width: 100%; padding: 0 22px; height: 46px; background: #ffffff; color: #3c4043;
  border: 1px solid #dadce0; border-radius: 4px; font-family: 'Roboto', 'Inter', sans-serif;
  font-size: 15px; font-weight: 500; letter-spacing: 0.2px; cursor: pointer;
  transition: background 0.15s ease, box-shadow 0.15s ease;
}
.gsi-button:hover { background: #f8f9fa; box-shadow: 0 1px 2px rgba(60, 64, 67, 0.18); }
.gsi-button:active { background: #f1f3f4; }
.gsi-button:disabled { opacity: 0.6; cursor: default; }
.gsi-icon { display: flex; align-items: center; }
.gsi-label { white-space: nowrap; }
#gate-result:empty { display: none; }
#gate-result .notice { margin-top: 16px; text-align: left; }
@media (max-width: 480px) { .gate-body { padding-top: 104px; } .gate-card { padding: 28px 22px 22px; } }

/* ----- Shell: sidebar + content ----- */
main { max-width: 1400px; margin: 0 auto; padding: 130px 28px 80px; }
.shell { display: grid; grid-template-columns: 212px minmax(0, 1fr); gap: 24px; align-items: start; transition: grid-template-columns 0.18s ease; }

.sidebar {
  position: sticky; top: 104px; display: flex; flex-direction: column; gap: 6px;
  background: var(--panel); border-radius: 16px; padding: 8px; backdrop-filter: blur(18px);
}
.side-item {
  display: flex; align-items: center; gap: 10px; padding: 11px 14px; border-radius: 11px;
  border: 0; background: transparent; color: var(--muted); font: inherit; font-size: 14px;
  font-weight: 600; cursor: pointer; text-align: left; white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}
.side-item:hover { background: rgba(255, 255, 255, 0.05); color: #fff; }
.side-item.active { background: rgba(255, 255, 255, 0.12); color: #fff; }
.side-item svg { flex: 0 0 auto; opacity: 0.9; }

/* Collapse control sits at the foot of the rail and is desktop-only (the phone
   renders the sidebar as a horizontal row, where there is nothing to collapse). */
.side-collapse {
  display: flex; align-items: center; gap: 10px; margin-top: 6px;
  padding: 10px 14px; border: 0; background: transparent; color: var(--faint);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  border-radius: 11px; white-space: nowrap; transition: background 0.15s ease, color 0.15s ease;
}
.side-collapse:hover { background: rgba(255, 255, 255, 0.05); color: #fff; }
.side-collapse svg { flex: 0 0 auto; transition: transform 0.18s ease; }

/* Collapsed rail: icons only, shell column shrinks. */
#app.sidebar-collapsed .shell { grid-template-columns: 64px minmax(0, 1fr); }
#app.sidebar-collapsed .side-item,
#app.sidebar-collapsed .side-collapse { justify-content: center; padding: 11px 6px; }
#app.sidebar-collapsed .side-item span,
#app.sidebar-collapsed .side-collapse span { display: none; }
#app.sidebar-collapsed .side-collapse svg { transform: rotate(180deg); }

@media (max-width: 900px) {
  .side-collapse { display: none; }
  #app.sidebar-collapsed .shell { grid-template-columns: 1fr; }
}

@media (max-width: 900px) {
  main { padding: 106px 16px 60px; }
  .shell { grid-template-columns: 1fr; gap: 16px; }
  .sidebar { position: static; flex-direction: row; overflow-x: auto; }
  .side-item { flex: 1; justify-content: center; }
}
@media (max-width: 640px) {
  main { padding: 100px 14px 56px; }
  .card { padding: 16px; border-radius: 16px; }
  .side-item { font-size: 13px; padding: 10px 12px; }
}

/* ----- Cards ----- */
.card {
  background: var(--panel);
  border-radius: 18px; padding: 20px; backdrop-filter: blur(18px);
  /* A hairline inner highlight reads as a glass edge without a hard stroke. */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.07);
}
.card h2 {
  margin: 0 0 16px; font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--title); font-weight: 700;
}
.card h2 .aside { float: right; text-transform: none; letter-spacing: 0; font-weight: 500; color: var(--muted); }
.card + .card { margin-top: 20px; }
.grid { display: grid; gap: 20px; }
.grid.two { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
@media (max-width: 900px) { .grid.two { grid-template-columns: 1fr; } }
/* Cards sitting side by side in a grid are already spaced by `gap` — the
   adjacent-sibling margin above would push the second one down. */
.grid > .card + .card { margin-top: 0; }

.notice { border-radius: 12px; padding: 12px 14px; font-size: 13px; margin-top: 14px; line-height: 1.55; }
.notice.err { background: rgba(255, 107, 99, 0.12); }
.notice.ok { background: rgba(89, 237, 151, 0.1); }
.notice.warn { background: rgba(255, 203, 69, 0.1); }
/* Neutral, informational — explains an expected 0 without looking like an error. */
.notice.info { background: rgba(255, 255, 255, 0.055); color: var(--muted); }
.notice.info b { color: rgba(255, 255, 255, 0.88); font-weight: 600; }
.notice code { font-family: ui-monospace, Menlo, monospace; font-size: 12px; background: rgba(0, 0, 0, 0.3); padding: 1px 5px; border-radius: 5px; }
.empty { color: var(--muted); font-size: 13px; padding: 8px 0; }
.muted { color: var(--muted); }

/* ----- Page head + sticky control bar -----
   The title scrolls away with the content; only the controls stick. Its blurred
   backing fades in once the bar is actually stuck (`.is-stuck`). */
.page-head { margin-bottom: 28px; }
.page-head h1 { font-size: 24px; margin: 0; font-weight: 700; letter-spacing: -0.3px; }
.page-head .sub { color: var(--muted); font-size: 13px; margin-top: 5px; }

.toolbar {
  position: sticky; top: 88px; z-index: 40;
  display: flex; align-items: center; justify-content: flex-end; gap: 12px; flex-wrap: wrap;
  margin-bottom: 20px;
}
.toolbar::before { content: none; }
/* The blurred backing is a full-width fixed layer rather than a pseudo on the
   sticky bar: the bar lives in the content column, so its box started 240px in
   and left a hard vertical edge beside the sidebar. */
.sticky-band {
  position: fixed; top: 0; left: 0; right: 0; height: 150px; z-index: 30;
  pointer-events: none; opacity: 0; transition: opacity 0.2s ease;
  background: rgba(0, 19, 31, 0.72);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 58%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 0%, #000 58%, transparent 100%);
}
#view-dashboard.is-stuck .sticky-band { opacity: 1; }
@media (max-width: 700px) { .sticky-band { display: none; } }

/* Grid and flex children default to `min-width: auto`, so a single wide
   descendant — the five range tabs, which need ~408px — was widening .toolbar,
   .kpis and every tile to 408px inside a 390px viewport, clipped by
   `overflow-x: hidden` on the body. Allowing these to shrink contains it. */
.shell > *,
.content,
.toolbar > *,
.kpis > *,
.kpi-group,
.kpi-row > *,
.dl-tabs-row > * { min-width: 0; }

.segments { display: flex; gap: 4px; background: rgba(255, 255, 255, 0.03); border-radius: 999px; padding: 4px; }
.segments button {
  display: inline-flex; align-items: center; gap: 7px;
  border: 0; background: transparent; color: rgba(255, 255, 255, 0.45); padding: 9px 14px;
  border-radius: 999px; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
  white-space: nowrap; transition: background 0.15s ease, color 0.15s ease;
}
.segments button.active { background: rgba(255, 255, 255, 0.12); color: #fff; }
/* Platform marks used by the download tabs. */
.plogo { flex: 0 0 auto; opacity: 0.7; }
.segments button.active .plogo { opacity: 1; }
.dl-switch button .plogo { opacity: 0.85; }
.segments button:disabled { cursor: default; }
.segments button.active:disabled { color: var(--green); animation: pulseText 1.1s ease-in-out infinite; }
@keyframes pulseText { 50% { color: rgba(89, 237, 151, 0.45); } }

/* Compact range labels on phones. The whole control cluster — filter plus a
   refresh icon in its own slot — docks to the bottom edge, within thumb reach. */
.rng-sm { display: none; }
/* One refresh control everywhere: a small icon button. The icon doubles as the
   busy indicator, so there is no need for a separate spinner on phones. */
#refresh { flex: 0 0 auto; padding: 11px; border-radius: 12px; }
#refresh.is-busy .refresh-icon { animation: spin 0.9s linear infinite; }
@media (max-width: 700px) {
  .toolbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 900; top: auto;
    display: flex; align-items: center; justify-content: flex-start; gap: 10px; flex-wrap: nowrap;
    margin: 0;
    padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
    background: rgba(0, 19, 31, 0.9);
    backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  }
  .toolbar::before { content: none; }
  .ranges-wrap { flex: 1 1 auto; min-width: 0; }
  .ranges-wrap .segments { width: 100%; box-sizing: border-box; }
  /* The standalone spinner would shift the filter row as it comes and goes, so
     on phones the spinning refresh icon is the only indicator. */
  #spinner { display: none; }
  /* The filter bar is docked to the bottom on phones, so the title does not need
     extra room beneath it. */
  .page-head { margin-bottom: 16px; }
  main { padding-bottom: calc(104px + env(safe-area-inset-bottom, 0px)); }
  .segments { flex: 1 1 auto; }
  .segments button { flex: 1 1 0; justify-content: center; padding: 8px 6px; }
  .rng-lg { display: none; }
  .rng-sm { display: inline; }
}

/* ----- Loading feedback while a range refetches ----- */
.spinner {
  width: 16px; height: 16px; flex: 0 0 auto; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.16); border-top-color: var(--green);
  animation: spin 0.7s linear infinite;
}
.spinner[hidden] { display: none; }
@keyframes spin { to { transform: rotate(360deg); } }
#view-dashboard .kpis,
#view-dashboard .card,
#view-dashboard .sources { transition: opacity 0.18s ease; }
#view-dashboard.is-loading .kpis,
#view-dashboard.is-loading .card,
#view-dashboard.is-loading .sources { opacity: 0.38; pointer-events: none; }

.btn {
  display: inline-flex; align-items: center; gap: 8px;
  border: 0; border-radius: 12px; padding: 12px 18px; font: inherit; font-weight: 600;
  cursor: pointer; background: var(--green); color: #041f14;
}
.btn:disabled { opacity: 0.5; cursor: default; }
.btn.ghost { background: rgba(255, 255, 255, 0.07); color: var(--text); }
.actions { display: flex; gap: 10px; align-items: center; margin-top: 18px; flex-wrap: wrap; }

/* ----- Dashboard: KPI groups -----
   Both groups carry three tiles, so they are given the same column count and
   the same width. Previously the range group was wider (1.3fr), so its tiles
   fitted three-across while the rolling group wrapped to two — leaving the two
   groups visibly different heights. */
.kpis { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 22px; margin-bottom: 20px; }
@media (max-width: 900px) { .kpis { grid-template-columns: 1fr; gap: 16px; } }
.kpi-group { display: flex; flex-direction: column; }
.kpi-group-title {
  display: flex; align-items: center; gap: 7px; margin: 0 0 10px;
  font-size: 11px; letter-spacing: 0.13em; text-transform: uppercase;
  color: var(--muted); font-weight: 700;
}
.kpi-group-title svg { opacity: 0.65; flex: 0 0 auto; }
.kpi-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; flex: 1 1 auto; }
@media (max-width: 620px) { .kpi-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.kpi {
  background: var(--panel-lift); border-radius: 14px; padding: 14px 15px; backdrop-filter: blur(18px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.09); height: 100%;
}
.kpi-head { display: flex; align-items: center; gap: 7px; }
.kpi-head svg { opacity: 0.75; flex: 0 0 auto; }
/* Change vs the previous window of the same length, pushed to the top-right. */
.kpi .delta {
  margin-left: auto; flex: 0 0 auto; font-size: 10.5px; font-weight: 700;
  font-variant-numeric: tabular-nums; padding: 2px 7px; border-radius: 999px;
  white-space: nowrap; letter-spacing: 0;
}
.kpi .delta.up { color: var(--green); background: rgba(89, 237, 151, 0.12); }
.kpi .delta.down { color: var(--red); background: rgba(255, 107, 99, 0.13); }
.kpi .delta.flat { color: var(--muted); background: rgba(255, 255, 255, 0.07); }
.kpi .label { font-size: 10.5px; letter-spacing: 0.11em; text-transform: uppercase; color: rgba(255, 255, 255, 0.68); font-weight: 700; }
.kpi .value { font-size: 26px; font-weight: 700; margin-top: 9px; letter-spacing: -0.5px; font-variant-numeric: tabular-nums; }
.kpi .hint { font-size: 11.5px; color: var(--faint); margin-top: 5px; line-height: 1.45; }
/* Collapsed explanatory note under the KPI groups. */
.kpi-note { margin: -6px 0 20px; }
.kpi-note:empty { display: none; }

/* ----- Dashboard: chart ----- */
.chart-wrap { width: 100%; }
.chart-wrap svg { display: block; width: 100%; height: auto; overflow: visible; }
.legend { display: flex; gap: 18px; margin-top: 12px; font-size: 12.5px; color: var(--muted); flex-wrap: wrap; }
.legend span { display: inline-flex; align-items: center; gap: 8px; }
.swatch { width: 11px; height: 11px; border-radius: 3px; }

/* ----- Dashboard: ranked lists ----- */
.rank { display: flex; flex-direction: column; gap: 2px; }
.rank .row { position: relative; display: flex; align-items: center; gap: 12px; padding: 9px 10px; border-radius: 10px; overflow: hidden; }
.rank .fill { position: absolute; inset: 0 auto 0 0; background: rgba(89, 237, 151, 0.09); border-radius: 10px; }
.rank .name { position: relative; flex: 1; min-width: 0; font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rank .name .flag { margin-right: 8px; }
.rank .val { position: relative; font-size: 13.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
.rank .pct { position: relative; font-size: 12px; color: var(--muted); width: 46px; text-align: right; font-variant-numeric: tabular-nums; }

/* ----- Dashboard: tables ----- */
table.data { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.data th {
  text-align: left; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted); font-weight: 600; padding: 0 10px 10px; border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
table.data td { padding: 9px 10px; border-bottom: 1px solid rgba(255, 255, 255, 0.04); }
table.data tr:last-child td { border-bottom: 0; }
table.data .num { text-align: right; font-variant-numeric: tabular-nums; }
table.data .mono { font-family: ui-monospace, Menlo, monospace; font-size: 12.5px; }
/* The Users column is the one people actually read, so it is lifted slightly:
   a soft tint, brighter and bolder figures, and the count beside it dimmed. */
table.data .users { background: rgba(89, 237, 151, 0.055); padding-left: 14px; padding-right: 14px; }
table.data th.users { color: rgba(255, 255, 255, 0.86); border-radius: 8px 8px 0 0; }
table.data td.users { color: #ffffff; font-weight: 600; }
table.data tr:last-child td.users { border-radius: 0 0 8px 8px; }
table.data .num:not(.users) { color: rgba(255, 255, 255, 0.6); }
.scroll-y { max-height: 340px; overflow: auto; }
.scroll-y.tall { max-height: 560px; }
.scroll-y::-webkit-scrollbar { width: 8px; }
.scroll-y::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.12); border-radius: 4px; }

/* Source pills live at the bottom of the page, after all the panels. */
/* The three sources share one card, laid out as a neat grid of tiles. */
.sources { display: grid; grid-template-columns: repeat(auto-fit, minmax(224px, 1fr)); gap: 12px; }
.source {
  display: flex; align-items: flex-start; gap: 10px;
  background: rgba(255, 255, 255, 0.04); border-radius: 12px; padding: 12px 14px;
  font-size: 13px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.source .dot { margin-top: 5px; }
.source .meta { line-height: 1.45; }
.source .meta b { font-weight: 600; }
.source .meta .role { color: var(--muted); font-size: 11.5px; }

/* "All events" disclosure button. */
.all-events-btn { padding: 10px 16px; font-size: 13px; }
.all-events-btn svg { transition: transform 0.18s ease; }
.all-events-btn[aria-expanded='true'] svg { transform: rotate(180deg); }

/* Search inside the All events panel. Only the results repaint on input, so the
   input keeps focus and the caret. */
.events-toolbar { display: flex; align-items: center; gap: 12px; margin-top: 16px; }
.events-search {
  flex: 1 1 auto; min-width: 0; width: 100%;
  background: transparent; border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  color: var(--text); padding: 10px 13px; font: inherit; font-size: 13px; outline: none;
  -webkit-appearance: none; appearance: none;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.events-search:focus { border-color: rgba(255, 255, 255, 0.26); background: rgba(255, 255, 255, 0.02); }
.events-search::placeholder { color: var(--faint); }
.events-search::-webkit-search-cancel-button { -webkit-appearance: none; }
.events-count { font-size: 12px; color: var(--muted); white-space: nowrap; flex: 0 0 auto; }

/* ----- Settings: members ----- */
.member-add { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.member-add input {
  flex: 1 1 240px; min-width: 0; background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.12); border-radius: 12px;
  color: var(--text); padding: 11px 13px; font: inherit; font-size: 13.5px; outline: none;
  transition: border-color 0.15s ease;
}
.member-add input:focus { border-color: rgba(255, 255, 255, 0.26); }
.member-add select { flex: 0 0 130px; }
.member-add .btn { flex: 0 0 auto; }

.member-list { display: flex; flex-direction: column; }
.member-row {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.member-row:last-child { border-bottom: 0; }
.member-info { flex: 1; min-width: 0; }
.member-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.member-email { font-size: 13.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.member-meta { font-size: 11.5px; color: var(--faint); margin-top: 3px; }
.member-remove { flex: 0 0 auto; padding: 8px 14px; font-size: 12.5px; }

.role-tag {
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 999px; white-space: nowrap;
}
.role-owner { color: var(--green); background: rgba(89, 237, 151, 0.13); }
.role-admin { color: #7aa7ff; background: rgba(79, 124, 255, 0.16); }
.role-member { color: var(--muted); background: rgba(255, 255, 255, 0.07); }
.role-you { color: #041f14; background: var(--green); }

.member-note { margin-top: 16px; font-size: 12px; color: var(--faint); line-height: 1.6; }
.member-note p { margin: 0 0 5px; }
.member-note p:last-child { margin-bottom: 0; }
.member-note b { color: rgba(255, 255, 255, 0.8); font-weight: 600; }
.member-note code { font-family: ui-monospace, Menlo, monospace; font-size: 11px; }

@media (max-width: 640px) {
  .member-row { flex-direction: column; align-items: flex-start; gap: 8px; }
  .member-email { white-space: normal; }
}

/* Unused-events section at the end of the All events panel. No divider rule —
   spacing and the heading carry the separation, matching the no-stroke style. */
.unused-events { margin-top: 26px; }
.unused-events h4 {
  display: flex; align-items: center; gap: 8px; margin: 0 0 8px;
  font-size: 12px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--title); font-weight: 700;
}
.unused-events h4 .count { letter-spacing: 0; color: var(--muted); font-weight: 600; }
.unused-hint { margin: 0 0 12px; font-size: 12px; color: var(--faint); line-height: 1.55; }
.unused-hint code { font-family: ui-monospace, Menlo, monospace; font-size: 11px; color: var(--muted); }
.unused-list { display: flex; flex-wrap: wrap; gap: 8px; }
.unused-item {
  font-family: ui-monospace, Menlo, monospace; font-size: 12px; color: var(--muted);
  background: rgba(255, 255, 255, 0.045); border-radius: 8px; padding: 6px 10px;
}
.source .meta { line-height: 1.35; }
.source .meta b { font-weight: 600; }
.source .meta .role { color: var(--muted); font-size: 11.5px; }

.big-number { font-size: 40px; font-weight: 800; letter-spacing: -1px; font-variant-numeric: tabular-nums; }
.big-label { color: var(--muted); font-size: 12.5px; margin-top: 4px; }

/* ----- Dashboard: downloads ----- */
.dl-tabs-row { display: flex; margin-bottom: 20px; }
.dl-figure { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.dl-figure .n {
  font-size: 46px; font-weight: 800; letter-spacing: -1.5px; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.dl-figure .n.total { color: var(--green); }
.dl-figure .cap { min-width: 0; }
.dl-figure .cap-title { color: rgba(255, 255, 255, 0.92); font-size: 14px; font-weight: 600; letter-spacing: -0.1px; }
.dl-figure .cap-sub { color: var(--muted); font-size: 12.5px; margin-top: 5px; }
.dl-switch { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 18px; }
.dl-switch button {
  display: inline-flex; align-items: baseline; gap: 8px; border: 0; cursor: pointer;
  font: inherit; font-size: 12.5px; color: var(--muted);
  background: rgba(255, 255, 255, 0.05); border-radius: 999px; padding: 7px 14px;
  transition: background 0.15s ease, color 0.15s ease;
}
.dl-switch button:hover { background: rgba(255, 255, 255, 0.09); color: #fff; }
.dl-switch button b { color: #fff; font-weight: 700; font-variant-numeric: tabular-nums; }
.dl-switch-unique {
  display: inline-flex; align-items: baseline; gap: 8px; font-size: 12.5px; color: var(--faint);
  padding: 7px 4px;
}
.dl-switch-unique b { color: rgba(255, 255, 255, 0.8); font-weight: 700; font-variant-numeric: tabular-nums; }
/* Collapsed explanatory notes — a native <details>, collapsed by default. The
   generic `details summary` marker rules live with the push form styles. */
.notes { margin-top: 18px; }
.notes .notes-body { margin-top: 10px; font-size: 12px; color: var(--muted); line-height: 1.6; }
.notes .notes-body p { margin: 0 0 6px; }
.notes .notes-body p:last-child { margin-bottom: 0; }
.notes .notes-body b { color: rgba(255, 255, 255, 0.82); font-weight: 600; }
.notes .notes-body code {
  font-family: ui-monospace, Menlo, monospace; font-size: 11px;
  background: rgba(0, 0, 0, 0.3); padding: 1px 4px; border-radius: 4px;
}

/* ----- Dashboard: compact mobile tuning ----- */
@media (max-width: 640px) {
  /* Ranked lists: keep one row per item rather than wrapping onto three lines. */
  .rank .row { gap: 8px; padding: 8px; }
  .rank .name { font-size: 13px; }
  .rank .val { font-size: 13px; }
  .rank .pct { width: 38px; font-size: 11.5px; }
  /* Tables: fixed layout so long event names ellipsis instead of stretching. */
  table.data { table-layout: fixed; font-size: 12.5px; }
  table.data th, table.data td { padding: 8px 6px; }
  table.data th:first-child, table.data td:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  table.data .num { width: 58px; }
  .notes { margin-top: 14px; }
}

/* ----- Dashboard: user base ----- */
.ub-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; }
.ub-stat {
  background: rgba(255, 255, 255, 0.04); border-radius: 12px; padding: 14px 16px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.ub-stat.primary { background: rgba(89, 237, 151, 0.09); box-shadow: inset 0 1px 0 rgba(89, 237, 151, 0.18); }
.ub-stat .l { font-size: 10.5px; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(255, 255, 255, 0.68); font-weight: 700; }
.ub-stat .v { font-size: 28px; font-weight: 700; margin-top: 8px; letter-spacing: -0.5px; font-variant-numeric: tabular-nums; }
.ub-stat.primary .v { color: var(--green); }
.ub-stat .h { font-size: 11.5px; color: var(--faint); margin-top: 5px; line-height: 1.45; }
@media (max-width: 640px) {
  .dl-figure { align-items: flex-start; gap: 14px; }
  .dl-figure .n { font-size: 38px; }
}

/* ----- Push console: form ----- */
.push-grid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); gap: 20px; align-items: stretch; }
/* The right column matches the composer's height and its list scrolls, rather
   than sizing the whole row to the full send history. The list is absolutely
   positioned so it contributes no intrinsic height to the grid row — the
   composer drives the height and the list fills and scrolls inside it. */
.push-grid > section:last-child { display: flex; flex-direction: column; min-height: 0; }
.push-grid > section:last-child > .card { position: relative; flex: 1 1 auto; min-height: 300px; }
.push-grid > section:last-child > .card .history {
  position: absolute; top: 52px; right: 20px; bottom: 20px; left: 20px; overflow-y: auto;
}
@media (max-width: 900px) {
  .push-grid { grid-template-columns: 1fr; }
  .push-grid > section:last-child > .card { min-height: 0; }
  .push-grid > section:last-child > .card .history { position: static; max-height: 340px; }
}

label { display: block; font-size: 12px; color: var(--muted); margin: 0 0 6px; }
input[type="text"], textarea, select {
  width: 100%; background-color: rgba(0, 0, 0, 0.3); border: 0;
  border-radius: 12px; color: var(--text); padding: 11px 13px; font: inherit; outline: none;
}
/* Hint text in the push form sits further back than the browser default so it
   cannot be mistaken for real content. */
.field input::placeholder, .field textarea::placeholder { color: rgba(255, 255, 255, 0.26); }
input[type="text"]:focus, textarea:focus, select:focus { background-color: rgba(0, 0, 0, 0.45); }
/* Draw our own select arrow so it isn't flush against the edge. */
select {
  appearance: none; -webkit-appearance: none; padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='16'%20height='16'%20viewBox='0%200%2016%2016'%20fill='none'%3E%3Cpath%20d='M4%206l4%204%204-4'%20stroke='%23ffffff'%20stroke-opacity='0.5'%20stroke-width='1.6'%20stroke-linecap='round'%20stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 13px center; background-size: 15px 15px;
}
textarea { resize: vertical; min-height: 96px; }
.field + .field { margin-top: 14px; }
/* Scoped to the push form. A bare `.row` rule also matched the dashboard's
   ranked-list rows (`.rank .row`), and its mobile `flex-direction: column`
   stacked each country/value/percentage onto three lines. */
.field.row { display: flex; gap: 12px; }
.field.row > * { flex: 1; }
@media (max-width: 640px) { .field.row { flex-direction: column; gap: 14px; } }

.history { font-size: 13px; }
.history::-webkit-scrollbar { width: 8px; }
.history::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.12); border-radius: 4px; }
.history .item { padding: 10px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.06); }
.history .item:last-child { border-bottom: 0; }
.history .item .top { display: flex; justify-content: space-between; gap: 12px; }
.history .item .when { color: var(--muted); font-size: 12px; }
.history .item .body { color: var(--muted); font-size: 12px; margin-top: 2px; }

.kv { display: flex; gap: 8px; margin-bottom: 8px; }
.kv input { flex: 1; }
.kv select { flex: 1; }
.kv button { flex: 0 0 auto; padding-left: 14px; padding-right: 14px; }
.hint { font-size: 12px; color: var(--muted); margin-top: 6px; line-height: 1.5; }

details summary { cursor: pointer; font-size: 12px; color: var(--muted); list-style: none; user-select: none; }
details summary::-webkit-details-marker { display: none; }
details summary::before { content: '▸ '; }
details[open] summary::before { content: '▾ '; }

.preview-count { font-size: 12px; color: var(--muted); margin-top: 8px; }
.preview-count b { color: var(--green); }

/* Character counters — iOS truncates silently past these limits */
.counter {
  font-size: 11.5px; color: var(--muted); margin-top: 5px;
  text-align: right; font-variant-numeric: tabular-nums; transition: color 0.15s ease;
}
.counter.warn { color: var(--amber); }
.counter.over { color: var(--red); font-weight: 600; }
