/* ============================================================
   panels.css — cards, badges, tables, log console, buttons
   ============================================================ */

/* ---- Panel card ---- */
.panel {
  background: var(--bg-2);
  border: 1px solid var(--border-1);
  border-radius: var(--radius-2);
  padding: 16px 18px 18px;
  box-shadow: var(--shadow-panel);
  display: flex;
  flex-direction: column;
  min-width: 0;
  position: relative;
  transition: border-color var(--t-fast), transform var(--t-fast);
}
.panel:hover { border-color: var(--border-2); }

.panel-head {
  display: flex; align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}
.panel-icon {
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border-radius: var(--radius-1);
  font-size: 13px;
  flex-shrink: 0;
}
.panel-title {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-1);
}

/* GH #290: a panel-head's title + subtitle need their own stacked-column
   wrapper, distinct from .panel-head-extras (action buttons), which stays
   in .panel-head's row layout so it's still pushed to the right via its
   own margin-left:auto. Previously .panel-sub was styled only inside the
   one-off #mr-overview scope (pages.css) instead of here alongside the
   rest of this shared header component, so every other panel reusing this
   exact same markup (Dashboard's own Reader Pool panel, Reader
   Management's Live Reader Connections panel) rendered its subtitle at
   default browser text size crammed onto the same line as the title and
   action buttons -- unstyled by any panel CSS at all. */
.panel-head-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.panel-sub {
  font-size: 0.8rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  color: var(--text-muted, #94a3b8);
}

/* Accent colorings per panel (icon background + title color) */
.panel.acc-cyan   .panel-icon { background: var(--accent-cyan-dim);   color: var(--accent-cyan); }
.panel.acc-cyan   .panel-title{ color: var(--accent-cyan); }
.panel.acc-violet .panel-icon { background: var(--accent-violet-dim); color: var(--accent-violet); }
.panel.acc-violet .panel-title{ color: var(--accent-violet); }
.panel.acc-amber  .panel-icon { background: var(--accent-amber-dim);  color: var(--accent-amber); }
.panel.acc-amber  .panel-title{ color: var(--accent-amber); }
.panel.acc-blue   .panel-icon { background: var(--accent-blue-dim);   color: var(--accent-blue); }
.panel.acc-blue   .panel-title{ color: var(--accent-blue); }
.panel.acc-green  .panel-icon { background: var(--accent-green-dim);  color: var(--accent-green); }
.panel.acc-green  .panel-title{ color: var(--accent-green); }
.panel.acc-red    .panel-icon { background: var(--accent-red-dim);    color: var(--accent-red); }
.panel.acc-red    .panel-title{ color: var(--accent-red); }

.panel-head-extras {
  margin-left: auto;
  display: flex; align-items: center; gap: 8px;
}

/* ---- Key/value rows inside panels ---- */
.kv {
  display: flex; align-items: baseline;
  justify-content: space-between;
  padding: 7px 0;
  border-bottom: 1px solid var(--border-soft);
  font-size: 13px;
  gap: 12px;
}
.kv:last-child { border-bottom: 0; }
.kv-label { color: var(--text-3); }
.kv-value {
  color: var(--text-1);
  font-weight: 500;
  text-align: right;
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kv-value.num { font-variant-numeric: tabular-nums; }
.kv-value.mono { font-family: var(--font-mono); font-size: 12.5px; }

/* Big-state row at top of Reader Status */
.kv-hero {
  display: flex; flex-direction: column;
  gap: 2px;
  margin-bottom: 8px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border-1);
}
.kv-hero-label {
  font-size: 11.5px;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.kv-hero-value {
  font-size: 18px;
  font-weight: 700;
  color: var(--text-1);
}
.kv-hero-value.success { color: var(--accent-green); }
.kv-hero-value.warn    { color: var(--accent-amber); }
.kv-hero-value.error   { color: var(--accent-red); }

/* ---- Big number style (Events Summary / MQTT Status) ---- */
.kv-num {
  font-size: 22px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-1);
}
.kv-num.warn  { color: var(--accent-amber); }
.kv-num.error { color: var(--accent-red); }
.kv-num.success { color: var(--accent-green); }

/* ---- Badges ---- */
/* `:not([hidden])` matters: a plain `.badge { display: ... }` rule is an
   author-stylesheet rule, and author rules win over the browser's built-in
   `[hidden] { display: none }` UA rule regardless of specificity — so any
   badge toggled via the `hidden` attribute/property (e.g. config.js's
   "Unsaved changes" indicator) would stay visibly rendered even while
   `.hidden === true`. Scoping the class rule to `:not([hidden])` lets the
   attribute win as intended. */
.badge:not([hidden]) {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  line-height: 1;
}
.badge-neutral { background: rgba(255,255,255,0.06); color: var(--text-2); }
.badge-success { background: var(--accent-green-dim); color: var(--accent-green); }
.badge-warn    { background: var(--accent-amber-dim); color: var(--accent-amber); }
.badge-error   { background: var(--accent-red-dim);   color: var(--accent-red); }
.badge-info    { background: var(--accent-cyan-dim);  color: var(--accent-cyan); }
.badge-violet  { background: var(--accent-violet-dim);color: var(--accent-violet); }

/* GH #45: `forced-colors: active` (Windows/browser "High Contrast Mode") --
   never tested on this issue before, only prefers-color-scheme dark mode --
   found via a live headless-Chromium pass on Reader Management's Status /
   Reader Type / Name(primary) columns that `.badge` is the one pill-shaped
   component in this file with NO border at all: `.role-badge` and
   `.status-pill` (pages.css) both already ship `border: 1px solid
   var(--border)` and stay visibly bounded chips under forced-colors, but
   `.badge`'s only structure is its `rgba(...)` dim background -- and
   Chromium does not force-override low-alpha backgrounds (they're assumed
   to already blend safely with any canvas), so it renders as a near-
   invisible tint on the forced white/black Canvas with zero boundary.
   Combined with forced-colors also collapsing every variant's distinct
   text color to one system color, badge-success/warn/error/info/neutral
   all became fully identical, unbounded plain text -- e.g. Reader
   Management's Status column ("online"/"offline"/"unknown") lost all
   visual differentiation AND its pill shape at once. Scoped to
   forced-colors only (not a permanent visual change): a real 1px border
   survives forced-colors the same way `.data-table` cell borders and
   `.status-pill`/`.role-badge` already do, restoring a visible chip
   boundary; `currentColor` (the default when no border-color is given)
   automatically matches whatever system color forced-colors assigns to
   this badge's own (forced) text, no hardcoded color keyword needed. */
@media (forced-colors: active) {
  .badge:not([hidden]) { border: 1px solid; }
}

.badge-square {
  border-radius: var(--radius-1);
  padding: 4px 10px;
}

/* ---- Status dots ---- */
.dot {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--text-4);
  flex-shrink: 0;
}
.dot.dot-lg { width: 10px; height: 10px; }
.dot.dot-success { background: var(--accent-green); box-shadow: 0 0 0 3px var(--accent-green-dim); }
.dot.dot-warn    { background: var(--accent-amber); box-shadow: 0 0 0 3px var(--accent-amber-dim); }
.dot.dot-error   { background: var(--accent-red);   box-shadow: 0 0 0 3px var(--accent-red-dim); }
.dot.dot-info    { background: var(--accent-cyan);  box-shadow: 0 0 0 3px var(--accent-cyan-dim); }
.dot.dot-pulse   { animation: dot-pulse 1.4s ease-in-out infinite; }
@keyframes dot-pulse {
  0%, 100% { box-shadow: 0 0 0 3px var(--accent-green-dim); }
  50%      { box-shadow: 0 0 0 6px rgba(34,197,94,0.08); }
}

/* ---- Buttons ---- */
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 13px;
  border-radius: var(--radius-1);
  background: var(--bg-3);
  color: var(--text-1);
  font-size: 12.5px;
  font-weight: 600;
  border: 1px solid var(--border-2);
  transition: transform var(--t-fast), background var(--t-fast),
              border-color var(--t-fast), opacity var(--t-fast);
}
.btn:hover { background: var(--border-1); border-color: var(--border-2); }
.btn:active { transform: translateY(1px); }
.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.5; cursor: not-allowed;
}

.btn-success {
  background: var(--accent-green); color: #04140a;
  border-color: var(--accent-green);
}
.btn-success:hover { background: #34d36a; border-color: #34d36a; }

.btn-info {
  background: var(--accent-blue); color: #04111f;
  border-color: var(--accent-blue);
}
.btn-info:hover { background: #5197ff; border-color: #5197ff; }

.btn-danger {
  background: var(--accent-red); color: #1a0606;
  border-color: var(--accent-red);
}
.btn-danger:hover { background: #f56565; border-color: #f56565; }

.btn-ghost {
  background: transparent; border-color: var(--border-2); color: var(--text-2);
}
.btn-ghost:hover { background: var(--bg-3); color: var(--text-1); }

/* button group (start/stop trio in Inventory Control) */
.btn-group {
  display: flex; gap: 8px;
  flex-wrap: wrap;
}

/* ---- Tables ---- */
/* GH #230 Phase 3: .rp-table-wrap shares .table-wrap's styling but is a
   distinct class so the two new reader-pool tables (Dashboard, Reader
   Management) never get picked up by a bare `.table-wrap` selector meant
   for a specific page's one primary table (e.g. reader_mgmt's #rmTable). */
.table-wrap, .rp-table-wrap {
  flex: 1;
  overflow-x: auto;
  border-radius: var(--radius-1);
}
.tbl {
  width: 100%;
  font-size: 12.5px;
}
.tbl thead th {
  text-align: left;
  font-weight: 600;
  font-size: 11px;
  color: var(--text-3);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 8px 10px 8px 0;
  border-bottom: 1px solid var(--border-1);
}
.tbl tbody td {
  padding: 9px 10px 9px 0;
  border-bottom: 1px solid var(--border-soft);
  color: var(--text-2);
  vertical-align: middle;
}
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl tbody tr:hover td { background: rgba(255,255,255,0.015); }
.tbl-mono { font-family: var(--font-mono); font-size: 12px; color: var(--accent-cyan); }
.tbl-num  { text-align: right; font-variant-numeric: tabular-nums; }

/* Empty state inside a panel (for tables and similar) */
.empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 32px 20px;
  color: var(--text-3);
  text-align: center;
}
.empty-icon {
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--bg-3);
  color: var(--text-3);
  margin-bottom: 10px;
  font-size: 16px;
}
.empty-title { font-size: 13px; font-weight: 500; color: var(--text-2); }
.empty-sub   { font-size: 12px; color: var(--text-3); margin-top: 3px; }

/* ---- Log console ---- */
.log-console {
  flex: 1;
  background: var(--bg-log);
  border-radius: var(--radius-1);
  padding: 10px 12px;
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--accent-green);
  max-height: 240px;
  overflow-y: auto;
  border: 1px solid var(--border-1);
}
.log-line { white-space: pre-wrap; }
.log-time { color: var(--text-3); }
.log-level { display: inline-block; min-width: 56px; }
.log-level.l-info  { color: var(--accent-cyan); }
.log-level.l-warn  { color: var(--accent-amber); }
.log-level.l-error { color: var(--accent-red); }
.log-msg.l-info  { color: var(--accent-green); }
.log-msg.l-warn  { color: var(--accent-amber); }
.log-msg.l-error { color: var(--accent-red); }

/* ---- System status banner ---- */
.system-banner {
  margin: 0 28px 18px;
  padding: 14px 18px;
  border-radius: var(--radius-2);
  background: linear-gradient(90deg,
              rgba(34,197,94,0.08) 0%,
              rgba(34,197,94,0.02) 50%,
              rgba(34,197,94,0.08) 100%);
  border: 1px solid rgba(34,197,94,0.30);
  display: flex; align-items: center; gap: 16px;
  flex-wrap: wrap;
}
.system-banner.is-warn {
  background: linear-gradient(90deg, rgba(251,191,36,0.08) 0%, rgba(251,191,36,0.02) 100%);
  border-color: rgba(251,191,36,0.30);
}
.system-banner.is-error {
  background: linear-gradient(90deg, rgba(239,68,68,0.08) 0%, rgba(239,68,68,0.02) 100%);
  border-color: rgba(239,68,68,0.30);
}
.banner-icon {
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--accent-green-dim);
  color: var(--accent-green);
}
.system-banner.is-warn  .banner-icon { background: var(--accent-amber-dim); color: var(--accent-amber); }
.system-banner.is-error .banner-icon { background: var(--accent-red-dim);   color: var(--accent-red); }
.banner-title {
  font-weight: 700;
  color: var(--text-1);
  font-size: 14px;
}
.banner-stat {
  font-size: 13px; color: var(--text-2);
}
.banner-grow { flex: 1; }

@media (max-width: 820px) {
  .system-banner { margin: 0 18px 14px; }
}

/* ===== Dashboard grids: show ~10 rows then scroll (QA #5) ===== */
#reads-wrap.table-wrap, #tag-events-wrap.table-wrap { max-height: 348px; overflow-y: auto; }
#reads-wrap.table-wrap thead th, #tag-events-wrap.table-wrap thead th {
  position: sticky; top: 0; z-index: 2; background: var(--bg-2);
}

/* QA #19/#20: numeric headers must right-align to match their data cells.
   (.tbl thead th defaults to left and out-specifies .tbl-num, so override it.) */
.tbl thead th.tbl-num { text-align: right; }
