/*
 * BAM! Admin — dark admin UI.
 * Restrained neutral palette, one accent (emerald) used sparingly for primary
 * actions and active states, 8px radii, subtle 1px borders. No gradients, no
 * external assets (CSP: default-src 'self'). Initially-hidden elements use the
 * .hidden class — inline style attributes are blocked by CSP.
 */
:root {
  --bg: #0a0a0b;
  --surface: #131316;
  --surface-2: #1a1a1f;
  --surface-3: #222228;
  --border: #26262c;
  --border-strong: #33333a;
  --text: #ececf1;
  --text-2: #a1a1ab;
  --text-3: #6b6b75;
  --accent: #10b981;
  --accent-hover: #0ea472;
  --accent-muted: rgba(16, 185, 129, .12);
  --danger: #ef4444;
  --danger-muted: rgba(239, 68, 68, .12);
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --radius: 8px;
  --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

.hidden { display: none !important; }

/* Dark-themed scrollbars everywhere — the OS default light/thin scrollbar
   reads badly against this palette and is easy to miss/mis-grab on a wide
   table (Firefox + WebKit both need their own syntax, no shorthand covers both). */
* { scrollbar-color: var(--border-strong) var(--surface); scrollbar-width: thin; }
*::-webkit-scrollbar { width: 12px; height: 12px; }
*::-webkit-scrollbar-track { background: var(--surface); }
*::-webkit-scrollbar-thumb {
  background: var(--border-strong); border-radius: 8px;
  border: 3px solid var(--surface); background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-3); background-clip: padding-box; }
*::-webkit-scrollbar-corner { background: var(--surface); }

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  font-size: 14px; line-height: 1.45;
  background: var(--bg); color: var(--text);
  height: 100vh; display: flex; flex-direction: column; overflow: hidden;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ── top bar ── */
header {
  display: flex; align-items: center; gap: 12px; flex-shrink: 0;
  height: 52px; padding: 0 20px;
  background: var(--surface); border-bottom: 1px solid var(--border);
}
header h1 { font-size: 14px; font-weight: 600; letter-spacing: -.01em; white-space: nowrap; flex-shrink: 0; }
header h1 .bang { color: var(--accent); }
header .spacer { flex: 1; min-width: 8px; }
/* min-width:0 is load-bearing on a narrow viewport: without it this flex
   item won't shrink below its own text's full width (same flexbox pitfall
   as the Raw Data table), which was pushing the logo AND the sign-out
   button into wrapping instead of just eliding the username/role text
   that doesn't fit. */
header .who {
  font-size: 12.5px; color: var(--text-2);
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#logout-btn { flex-shrink: 0; white-space: nowrap; }

/* ── buttons ── */
button {
  font: inherit; font-size: 13px; font-weight: 500;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  padding: 6px 14px; cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
button:hover { background: var(--surface-3); border-color: #3d3d46; }
button:focus-visible, input:focus-visible, select:focus-visible {
  outline: none; box-shadow: var(--focus-ring);
}
button.primary {
  background: var(--accent); border-color: var(--accent); color: #04150f;
  font-weight: 600;
}
button.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
button.danger:hover {
  background: var(--danger-muted); border-color: var(--danger); color: var(--danger);
}
button.small { padding: 3px 10px; font-size: 12px; border-radius: 6px; }
button:disabled { opacity: .4; cursor: not-allowed; }
button:disabled:hover { background: var(--surface-2); border-color: var(--border-strong); }

/* ── inputs ── */
input, select {
  font: inherit; font-size: 13px;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  padding: 7px 11px; outline: none;
  transition: border-color .12s;
}
input::placeholder { color: var(--text-3); }
input:focus, select:focus { border-color: var(--accent); }

/* ── tabs ── */
.tabs {
  display: flex; gap: 2px; padding: 0 20px; flex-shrink: 0;
  background: var(--surface); border-bottom: 1px solid var(--border);
}
.tab {
  padding: 10px 14px; font-size: 13px; font-weight: 500; color: var(--text-2);
  cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px;
  transition: color .12s, border-color .12s;
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--text); border-bottom-color: var(--accent); }

/* ── views & cards ── */
.view { display: none; flex: 1; min-height: 0; overflow: auto; padding: 24px 20px; }
.view.active { display: block; }
/* Raw Data owns its scrolling internally (.sidebar scrolls Y, .table-wrap
   scrolls X+Y) — the base .view's own overflow:auto was ALSO engaging
   whenever the table was wide, since nothing here constrained
   .viewer-layout's width against it. That meant two independent horizontal
   scrollbars (the outer .view and the inner .table-wrap), and scrolling
   the outer one dragged the whole flex row — sidebar included — sideways
   instead of just the table body. overflow:hidden here hands 100% of the
   scrolling to the inner containers, where it belongs. */
#view-raw.view.active { display: flex; padding: 0; overflow: hidden; }

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 10px; padding: 18px 20px; margin: 0 auto 20px;
  /* Uncapped width stretched the line/bar charts (fixed height) into an
     unreadably flat, elongated ribbon on wide monitors — this caps how much
     the charts can widen while still giving tables far more room than the
     old 1100px ceiling. Centered (margin: auto) instead of left-anchored so
     the leftover space on wide monitors is split evenly either side. */
  max-width: 1600px;
  /* Every table living in a card (Users, the Dashboard's accordion tables)
     is wider than a phone viewport. Without this, the overflow just clips
     silently at the card's edge — verified on an iPhone-14-width viewport,
     e.g. the "LAST"/"ERROR RATE"/"N" columns were cut off with no way to
     reach them at all. auto here gives each card its own contained
     horizontal scroll instead of a hard clip. */
  overflow-x: auto;
}
.card h2, .section-label {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .08em; color: var(--text-3); margin-bottom: 14px;
}

.form-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

.msg { font-size: 13px; margin-top: 10px; min-height: 18px; }
.msg.error { color: var(--danger); }
.msg.ok { color: var(--accent); }

/* ── tables ── */
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th {
  text-align: left; font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-3);
  padding: 6px 10px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
td {
  padding: 6px 10px; border-bottom: 1px solid var(--border);
  /* Measured on legacy_pharmacy_matches (13 columns): at 320px, 5 of them
     (legacy_id, legacy_name, legacy_email, evidence, matched_pharmacy_id)
     all clamped to the max, eating most of the viewport width for only
     6.4 of 13 columns visible without scrolling. Full values are one click
     away in the record modal anyway, so there's no real cost to truncating
     harder — narrower columns fit far more of the table on screen at once. */
  max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
tbody tr { transition: background .1s; }
tbody tr:hover td { background: var(--surface-2); }
tbody tr:last-child td { border-bottom: none; }
td.num { font-family: var(--mono); font-size: 12.5px; text-align: right; }
td .null { color: var(--text-3); font-style: italic; }
td .actions { display: flex; gap: 6px; flex-wrap: wrap; }

/* ── badges ── */
.badge {
  display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 600; letter-spacing: .01em;
}
.badge.ok { background: var(--accent-muted); color: var(--accent); }
.badge.bad { background: var(--danger-muted); color: var(--danger); }
.badge.role { background: var(--surface-3); color: var(--text-2); }

/* ── login page ── */
.login-wrap { flex: 1; display: flex; align-items: center; justify-content: center; padding: 20px; }
.login-card {
  width: 360px; background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 32px 28px;
}
.login-card label {
  display: block; font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-3); margin: 14px 0 6px;
}
.login-card label:first-of-type { margin-top: 0; }
.login-card input { width: 100%; }
.login-card button { width: 100%; margin-top: 20px; padding: 9px; }

/* ── raw data viewer ── */
.viewer-layout { display: flex; flex: 1; min-height: 0; min-width: 0; }
.sidebar {
  width: 240px; flex-shrink: 0; overflow-y: auto;
  background: var(--surface); border-right: 1px solid var(--border);
  padding: 12px 8px; display: flex; flex-direction: column; gap: 1px;
}
.sidebar .section-label { padding: 4px 10px 8px; margin: 0; }
.sidebar-table-item {
  display: flex; align-items: baseline; gap: 8px; padding: 6px 10px;
  border-radius: 6px; cursor: pointer; color: var(--text-2);
  font-family: var(--mono); font-size: 12.5px;
  transition: background .1s, color .1s;
}
.sidebar-table-item:hover { background: var(--surface-2); color: var(--text); }
.sidebar-table-item.active { background: var(--accent-muted); color: var(--accent); }
.sidebar-table-item .count { margin-left: auto; font-size: 11px; color: var(--text-3); }

/* Below phone/small-tablet width, a 240px fixed sidebar next to the table
   leaves an unusably narrow strip for the actual data (verified on an
   iPhone-14-width viewport: ~150px of table for a 390px screen). Stack the
   table list above the data instead of beside it, with the sidebar capped
   to a scrollable strip so the table below always gets most of the screen. */
@media (max-width: 720px) {
  .viewer-layout { flex-direction: column; }
  .sidebar {
    width: auto; max-height: 30vh; flex-shrink: 0;
    border-right: none; border-bottom: 1px solid var(--border);
  }
}
.sidebar-table-item.active .count { color: var(--accent); opacity: .7; }

.viewer-main { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.table-toolbar {
  display: flex; align-items: center; gap: 12px; flex-shrink: 0; flex-wrap: wrap;
  padding: 10px 16px; background: var(--surface); border-bottom: 1px solid var(--border);
}
.table-toolbar .tname { font-family: var(--mono); font-size: 13px; font-weight: 600; }
.table-toolbar .tcount { font-size: 12px; color: var(--text-3); }
.table-toolbar .spacer { flex: 1; min-width: 8px; }
.table-toolbar label { font-size: 12px; color: var(--text-2); display: flex; align-items: center; gap: 6px; }
.table-toolbar select { padding: 4px 8px; font-size: 12px; }
#viewer-search { width: 220px; max-width: 40vw; padding: 5px 10px; font-size: 12.5px; }
.viewer-no-matches {
  padding: 24px 16px; text-align: center; color: var(--text-3);
  font-size: 13px; font-style: italic;
}
/* Sticky first column: on a wide table you scroll right constantly, and
   without an anchored identifier column it's easy to lose track of which
   row you're even looking at. Sticky top (header) already existed; this
   adds sticky left for whichever column is first (almost always the row's
   real-world key — legacy_id, source, domain, etc). Needs an opaque
   background since sticky content must occlude whatever scrolls under it,
   and its own hover state so it doesn't look "stuck" when the rest of the
   row highlights. */
#viewer-table th:first-child, #viewer-table td:first-child {
  position: sticky; left: 0; background: var(--surface);
}
#viewer-table thead th:first-child { z-index: 3; }
#viewer-table tbody tr:hover td:first-child { background: var(--surface-2); }
/* min-width: 0 is load-bearing here: this is a flex item whose content is an
   HTML table, and tables resist shrinking below their intrinsic content width
   in a flexbox unless the item's min-width is explicitly overridden from the
   flex default of `auto`. Without it, .table-wrap just grows to the full
   table width instead of clipping to available space — which is exactly what
   let the OUTER .view container's own scrollbar mask the problem before (and
   why removing that outer scrollbar alone made the content unreachable
   instead of properly scrollable). */
.table-wrap { flex: 1; overflow: auto; min-height: 0; min-width: 0; }
.table-wrap thead { position: sticky; top: 0; z-index: 2; }
.table-wrap th { background: var(--surface); }
.viewer-empty { flex: 1; display: flex; align-items: center; justify-content: center; color: var(--text-3); font-size: 13px; }

/* ── dashboard ── */
.kpi-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 12px; margin: 0 auto 20px; max-width: 1600px;
}
.kpi-tile {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 10px; padding: 14px 16px;
}
.kpi-value {
  font-family: var(--mono); font-size: 22px; font-weight: 600;
  letter-spacing: -.02em; color: var(--text);
}
.kpi-label {
  font-size: 10.5px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .07em; color: var(--text-3); margin-top: 4px;
}

.chart-legend { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 10px; }
.legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-2); }
.legend-swatch { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }

.bar-chart { width: 100%; }
.bar-plot {
  display: flex; align-items: flex-end; gap: 2px;
  height: 190px; border-bottom: 1px solid var(--border-strong); padding-top: 8px;
}
.bar-col {
  flex: 1; min-width: 4px; height: 100%;
  display: flex; flex-direction: column-reverse;
  /* 2px surface-color gap between stacked segments — mark separation so
     adjacent source segments read as distinct marks (lead's convention). */
  gap: 2px; border-radius: 4px 4px 0 0; overflow: hidden;
  transition: background .1s;
}
.bar-col:hover { background: var(--surface-2); }
.bar-seg { width: 100%; }
.bar-axis { display: flex; gap: 2px; margin-top: 5px; }
.bar-axis span {
  flex: 1; min-width: 4px; font-size: 10px; color: var(--text-3);
  white-space: nowrap; overflow: visible;
}
.chart-scale { font-size: 11px; color: var(--text-3); margin-top: 6px; font-family: var(--mono); }

.line-chart-wrap { position: relative; width: 100%; }
.line-chart { width: 100%; height: 150px; display: block; }
.line-stroke { fill: none; stroke: var(--accent); stroke-width: 2; vector-effect: non-scaling-stroke; }
.line-area { fill: var(--accent-muted); stroke: none; }
/* Plain HTML dots positioned by % over the chart, NOT drawn inside the SVG.
   The SVG's viewBox is stretched non-uniformly (x and y scale differently once
   the card gets much wider than tall) so an SVG <circle> there renders as a
   squashed ellipse — measured ~3x wider than tall at 1600px card width. A
   left/top-positioned div's width/height are independent of that transform,
   so it stays a true circle at any chart width. */
.line-dot {
  position: absolute; width: 8px; height: 8px; margin: -6px 0 0 -6px;
  border-radius: 50%; background: var(--accent);
  /* 2px surface-color ring keeps each dot legible crossing the line/area fill. */
  border: 2px solid var(--surface); box-sizing: content-box;
  cursor: pointer;
}

/* ── chart tooltip (shared, real interactive hover — no native title attr) ── */
.chart-tooltip {
  position: fixed; z-index: 20; pointer-events: none;
  background: var(--surface-2); border: 1px solid var(--border-strong);
  border-radius: 6px; padding: 8px 10px;
  font-size: 12px; color: var(--text);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .5);
  max-width: 300px;
}
.chart-tooltip .tt-title { font-weight: 600; margin-bottom: 4px; }
.chart-tooltip .tt-row { display: flex; align-items: center; gap: 6px; color: var(--text-2); }
.chart-tooltip .tt-row + .tt-row { margin-top: 2px; }
.chart-tooltip .tt-swatch { width: 8px; height: 8px; border-radius: 2px; display: inline-block; flex-shrink: 0; }
.chart-tooltip .tt-total { margin-top: 5px; padding-top: 5px; border-top: 1px solid var(--border); color: var(--text); font-weight: 600; }

/* categorical palette for per-source chart segments (slots via app.js) */
.src-0 { background-color: #5b8cff; } /* canngo — blue */
.src-1 { background-color: #f59e0b; } /* flowzz — amber/orange */
.src-2 { background-color: #10b981; } /* weed_de — emerald */
.src-3 { background-color: #a78bfa; } /* cannaleo — violet */
.src-4 { background-color: #f472b6; }
.src-5 { background-color: #38bdf8; }
.src-6 { background-color: #facc15; }
.src-7 { background-color: #9ca3af; }

td.mono { font-family: var(--mono); font-size: 12.5px; }

/* ── dashboard accordion rows (click-to-expand) ── */
/* Same fix as #viewer-table tbody tr — no text selection on clickable rows. */
tr.expandable { cursor: pointer; user-select: none; }
tr.expandable td:first-child { padding-left: 22px; position: relative; }
tr.expandable td:first-child::before {
  content: '▸'; position: absolute; left: 6px; top: 50%;
  transform: translateY(-50%); font-size: 10px; color: var(--text-3);
  transition: transform .12s;
}
tr.expandable.expanded td { background: var(--surface-2); }
tr.expandable.expanded td:first-child::before { transform: translateY(-50%) rotate(90deg); }
tr.detail-row td {
  padding: 4px 16px 14px; max-width: none; overflow: visible; white-space: normal;
  background: var(--surface);
}
tr.detail-row:hover td { background: var(--surface); }
.detail-body {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 6px; padding: 10px 12px;
}
.detail-error { color: var(--danger); font-size: 12.5px; }
.detail-note { color: var(--text-3); font-size: 12px; margin-bottom: 8px; }
.detail-kv { display: grid; grid-template-columns: auto 1fr; gap: 2px 12px; font-size: 12.5px; }
.detail-kv .dk { color: var(--text-3); font-family: var(--mono); font-size: 11.5px; }
.detail-table { font-size: 12.5px; }
.detail-table th { font-size: 10.5px; padding: 4px 8px; white-space: nowrap; }
.detail-table td { padding: 4px 8px; max-width: none; overflow: visible; white-space: normal; }
.detail-table td.mono { font-family: var(--mono); font-size: 12px; }
.detail-pre {
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 6px; padding: 8px 10px;
  font-family: var(--mono); font-size: 12px; line-height: 1.5;
  overflow: auto; max-height: 150px; white-space: pre;
}

.info-box {
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 8px; padding: 12px 16px; margin: 0 auto 20px;
  font-size: 12.5px; color: var(--text-2); max-width: 1600px;
}

/* ── modal ── */
.modal-overlay {
  position: fixed; inset: 0; z-index: 10;
  background: rgba(0, 0, 0, .6); backdrop-filter: blur(2px);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.modal {
  width: 500px; max-width: 92vw;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: 12px; padding: 24px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, .5);
}
.modal h2 { font-size: 15px; font-weight: 600; margin-bottom: 8px; }
.modal .warn { color: var(--danger); font-size: 12.5px; font-weight: 500; }
.modal .token-box {
  background: var(--bg); border: 1px solid var(--border-strong); border-radius: var(--radius);
  padding: 12px 14px; margin: 14px 0 4px;
  font-family: var(--mono); font-size: 12.5px; word-break: break-all; user-select: all;
}
.modal .modal-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 16px; }
.modal .record-body {
  max-height: 60vh; overflow-y: auto;
  margin-top: 14px; padding-right: 4px;
}
.record-field { margin-bottom: 16px; }
.record-field:last-child { margin-bottom: 0; }
.record-label {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-3); margin-bottom: 4px;
  font-family: var(--mono);
}
.record-value { font-size: 13px; color: var(--text); word-break: break-word; white-space: pre-wrap; }
.record-value pre {
  margin: 0; background: var(--bg); border: 1px solid var(--border-strong);
  border-radius: var(--radius); padding: 12px 14px;
  font-family: var(--mono); font-size: 12.5px; line-height: 1.5;
  overflow-x: auto; white-space: pre;
}

/* ── raw viewer row affordance ── */
/* user-select:none is the actual fix here, not cursor:pointer — a real mouse
   click almost always has a pixel or two of drift between mousedown and
   mouseup, which a table full of plain selectable text interprets as a
   text-selection drag instead of a click. Different browsers/input devices
   handle "click fired after a selection happened" inconsistently, so rather
   than chase that per-browser behavior, remove the ambiguity at the root:
   these rows can never enter text-selection mode, so any mousedown+mouseup
   on one is unambiguously a click. */
#viewer-table tbody tr { cursor: pointer; user-select: none; }
