.admin-body { padding: 0; }
.admin-app.hidden { display: none; }
#addRowBtn.hidden { display: none; }
.admin-main { max-width: 900px; margin: 0 auto; padding: 14px 16px 60px; }

/* header/presence-list/settings-panel reuse style.css's rules verbatim -
   only the read-only badge and the sub-nav below are admin-specific */
.ro-badge {
  font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: 999px;
  background: var(--danger-bg); color: #b91c1c; white-space: nowrap;
}
.ro-badge.editable { background: #dcfce7; color: #15803d; }

.admin-tabs {
  display: flex; gap: 5px; flex-wrap: wrap;
}
.admin-tab {
  padding: 6px 10px; border-radius: 8px; border: 1px solid var(--border);
  background: var(--surface); color: var(--text); font-size: 12.5px; cursor: pointer;
  white-space: nowrap;
}
.admin-tab.active { background: var(--blue); color: #fff; border-color: var(--blue); }

.admin-tab-panel { display: none; }
.admin-tab-panel.active { display: block; }

.admin-hint { color: var(--muted); font-size: 13px; margin: 0 0 14px; }

/* ---------- view toggle (User Stats: list / timeline) ---------- */
.view-toggle { display: flex; gap: 4px; margin-bottom: 12px; }
.view-toggle-btn {
  padding: 5px 12px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--surface); color: var(--muted); font-size: 12.5px; cursor: pointer;
}
.view-toggle-btn.active { background: var(--blue); color: #fff; border-color: var(--blue); }
#statsList.hidden, #statsTimeline.hidden { display: none; }

/* ---------- User Stats timeline view ---------- */
.stats-timeline { border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px 14px; overflow-x: auto; }
.timeline-axis, .timeline-row { display: flex; align-items: center; gap: 10px; }
.timeline-name-spacer { flex: 0 0 110px; }
.timeline-name {
  flex: 0 0 110px; font-size: 12.5px; font-weight: 600; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.timeline-track { position: relative; flex: 1; min-width: 480px; height: 18px; }
.timeline-axis .timeline-track { height: 16px; margin-bottom: 6px; border-bottom: 1px solid var(--border); }
.timeline-tick {
  position: absolute; bottom: 2px; transform: translateX(-50%);
  font-size: 10px; color: var(--muted); white-space: nowrap;
}
.timeline-rows { display: flex; flex-direction: column; gap: 6px; }
.timeline-bar {
  position: absolute; top: 3px; height: 12px; border-radius: 6px; cursor: default;
  min-width: 4px;
}
.timeline-bar-live { box-shadow: 0 0 0 2px var(--surface), 0 0 0 3px currentColor; }

.day-clear-btn, .hour-clear-btn {
  padding: 3px 9px; border-radius: 6px; border: 1px solid var(--border);
  /* Was var(--danger, #b91c1c) - --danger was never actually defined
     anywhere, so this always resolved to that hardcoded fallback red
     regardless of theme. Fine in light mode; in dark mode that same fixed
     dark red sat on a dark background with too little contrast to read
     easily. var(--red) is the theme-aware token everything else already
     uses, and picks up dark mode's lighter red variant automatically. */
  background: var(--surface); color: var(--red); font-size: 11.5px; cursor: pointer;
}
.day-clear-btn:disabled, .hour-clear-btn:disabled { opacity: 0.5; cursor: default; }
.day-clear-btn.arming, .hour-clear-btn.arming { opacity: 0.6; cursor: wait; }
.day-clear-btn.armed, .hour-clear-btn.armed {
  background: var(--danger, #b91c1c); color: #fff; border-color: var(--danger, #b91c1c); opacity: 1;
}

.day-row { cursor: pointer; }
.day-row-empty { cursor: default; }
/* Was var(--surface) - the same flat grey used for idle rows/inputs
   elsewhere, which in light mode put muted or full-contrast text on a
   background barely different from the page itself and read as dull/low-
   contrast rather than "this row is open". --detail-tint is a dedicated,
   deliberately-light purple (same accent family as the rest of the admin
   panel) reserved for exactly this "currently-expanded detail" state, used
   for both this row and .day-hour-row below so the open day + its detail
   content read as one continuous tinted block instead of two different
   greys. */
.day-row.open { background: var(--detail-tint); }
.day-expand-cell { width: 22px; }
.day-expand-arrow { display: inline-block; color: var(--muted); font-size: 16px; line-height: 1; }
/* An expanded day's hour rows live in the *same* <table> as the day rows
   (see renderDaysTable in admin.js) so their columns are guaranteed to
   line up pixel-for-pixel with "Served"/"Left"/"Avg wait"/"Longest wait"/
   "Clear day" above - two separate tables each auto-sizing their own
   columns from their own content never reliably lines up, however closely
   their column order matches. Tinted background + smaller/quieter text
   marks them as subordinate detail rather than more top-level days. */
/* color was var(--muted) - Customer Stats' own quiet-detail-text
   convention, which paired with the light --detail-tint background above
   read as too low-contrast to be comfortable, especially in light mode.
   Full-contrast text here; .day-hour-label's leading dash keeps these rows
   visually subordinate to the day row above without needing muted text
   too. */
.day-hour-row { background: var(--detail-tint); font-size: 12.5px; color: var(--text); }
.day-hour-row td { border-bottom-color: var(--detail-border); }
.day-hour-label { padding-left: 4px; }
.day-hour-label::before { content: '–'; margin-right: 6px; color: var(--border); }

/* Live board tab reuses the main app's .rows/.row/.row-check/.row-content
   classes so it looks and behaves like the same list - see admin.js. Only
   disabled (read-only) checkboxes get the "inert" cursor; once read-only is
   switched off the checkboxes/text are genuinely editable, same as the
   main board. */
#liveRows .row-check:disabled { cursor: default; }
#liveRows .row.checked .row-display, #liveRows .row.checked .row-edit { opacity: 0.55; text-decoration: line-through; }
/* row-del is always present (same as Clear all) but visibly greyed out and
   inert while read-only is on, rather than missing entirely - mirrors
   .btn:disabled in style.css, which .row-del doesn't otherwise pick up
   since it isn't a .btn. */
#liveRows .row-del:disabled { opacity: 0.3; filter: grayscale(65%); cursor: default; pointer-events: none; }
.admin-live-empty, .admin-empty { color: var(--muted); font-size: 14px; padding: 12px 0; }

.admin-list { display: flex; flex-direction: column; gap: 8px; }
.admin-list-item {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--raised);
  flex-wrap: wrap;
}
.admin-list-item-main { display: flex; flex-direction: column; gap: 2px; min-width: 200px; }
.admin-list-name { font-weight: 600; font-size: 14px; }
.admin-list-sub { font-size: 12px; color: var(--muted); }
.admin-list-actions { display: flex; gap: 8px; }

.admin-day-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.admin-day-table th, .admin-day-table td {
  text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--border); vertical-align: top;
}
.admin-day-table th { color: var(--muted); font-weight: 600; font-size: 12px; }

/* Past days detail body - an expanded row's Timeline or List content lives
   here, inside a <td colspan> of the same table as the day rows (see
   renderPastDaysTable/renderPastDayDetail in admin.js). Unlike Customer
   Stats' hour breakdown (same table, same columns as the day rows above it
   - see renderDaysTable's comment), a session list has a completely
   different set of columns and Timeline is a chart, not a table, so that
   same-column trick doesn't apply here; this is a genuinely nested block
   instead. No left/right padding of its own - the outer cell's own padding
   (from .admin-day-table-wrap below) already insets it to line up with the
   "Date" column above, and adding more on top of that was what created the
   ugly gap before "Name"/the timeline axis. No border/background of its
   own either, for the same reason: it already sits inside the tinted
   .day-hour-row, wrapping it in a second box on top of that just looked
   boxed-within-boxed. .day-hour-row sets a muted text colour meant for
   Customer Stats' own quiet detail numbers, which are all explicitly
   recoloured where needed - our List table/Timeline content isn't, so it
   was silently inheriting that muted colour and reading as low-contrast.
   Reset it back to full-contrast text here; things that want to stay quiet
   (axis ticks, "No sessions.") already set their own muted colour
   explicitly and are unaffected. */
/* overflow-x: auto makes this cell's own content - a Timeline chart or a
   session-list table, either one often wider than a phone screen - scroll
   inside its own bounded frame, the same self-contained way Today's
   Timeline always has (nothing above/beside it moves). This replaced an
   earlier attempt that instead made the *whole* .admin-day-table-wrap
   below scroll - which dragged the Date/session-count row above it
   sideways along with everything else, reading as the entire page
   sliding around rather than one small chart panning inside its own
   frame. #statsPastDays's own table (see renderPastDaysTable in admin.js)
   uses table-layout:fixed specifically so it can never grow to swallow
   this cell's oversized content instead of leaving it to scroll here -
   without that, this overflow-x would never actually trigger, since the
   surrounding table would just stretch to fit it first. */
.day-detail-body { padding: 6px 0 2px; color: var(--text); overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* overflow-x: visible - this nested Timeline relies on .day-detail-body
   above for scrolling rather than having its own competing scroll
   container; two independently-scrollable regions nested inside each
   other is exactly the kind of thing that leaves a touchscreen unsure
   which one a sideways swipe is meant to move. */
.day-detail-body .stats-timeline { border: none; padding: 0; overflow-x: visible; }
/* Pins this table's column widths so it never grows past its container to
   accommodate an expanded row's oversized detail content (a 480px-wide
   Timeline track, or a 5-column session list) - without this, table
   layout's default behaviour is to stretch the whole table to fit its
   widest cell, which is what pushed .day-detail-body's overflow-x above
   out of reach (there was nothing left for it to scroll *within*). Fixed
   layout takes its column widths from the *header* row specifically, not
   .day-expand-cell's 22px on the body cells below it - the plain, class-
   less first <th> needed that same width spelled out here or every column
   would've come out evenly split instead, arrow column included. The
   remaining width splits evenly between "Date" and "Sessions" above (and,
   via colspan, becomes the fixed width .day-detail-body scrolls inside of
   below) - both are short values so an even split leaves plenty of room. */
#statsPastDays > .admin-day-table { table-layout: fixed; }
#statsPastDays > .admin-day-table th:first-child { width: 22px; }
.day-detail-body .admin-day-table { font-size: 12.5px; }
/* #statsPastDays-prefixed so this reliably beats .admin-day-table-wrap's
   own th/td padding rule below (same specificity otherwise - a plain
   .day-detail-body .admin-day-table td selector lost that fight simply by
   being declared earlier in the file, which was silently undoing the
   tighter spacing this block is trying to set). */
#statsPastDays .day-detail-body .admin-day-table th,
#statsPastDays .day-detail-body .admin-day-table td { padding: 6px 0; }
#statsPastDays .day-detail-body .admin-day-table td:not(:last-child),
#statsPastDays .day-detail-body .admin-day-table th:not(:last-child) { padding-right: 14px; }

/* Customer Stats tab - live "right now" numbers, an hour-of-day bar list
   for the last 7 days, and a plain by-day table. Reuses the same
   card/border/muted-text vocabulary as the rest of the admin panel rather
   than introducing a new visual language for one tab. */
.stat-cards { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 20px; }
.stat-card {
  flex: 1; min-width: 130px; padding: 12px 14px; border: 1px solid var(--border);
  border-radius: 10px; background: var(--raised);
}
.stat-card .stat-value { font-size: 22px; font-weight: 700; color: var(--text); line-height: 1.2; }
.stat-card .stat-label { font-size: 12px; color: var(--muted); margin-top: 2px; }
.stat-card.stat-warn .stat-value { color: var(--red); }

.customer-subhead { font-size: 13px; font-weight: 600; color: var(--muted); margin: 20px 0 8px; text-transform: uppercase; letter-spacing: 0.02em; }

/* Boxed to match .admin-day-table-wrap below it - previously a bare list of
   rows sitting directly under the heading, which read as disconnected from
   the boxed, card-like feel of the rest of the tab (stat cards, by-day
   table). Same border/radius/background vocabulary as those. */
#customerHourly {
  border: 1px solid var(--border); border-radius: 8px; background: var(--raised);
  padding: 14px 16px 12px;
}
/* Shared 4-column grid for both the header and every data row, so they stay
   pixel-aligned without the header needing to fake being a zero-data row
   (see the "why not just reuse .hour-bar-row" note in admin.js). The gap
   between the label/track and the two number columns is deliberately wider
   than the row's own internal rhythm - tight column-gap read as the
   customer-count and avg-wait numbers running into each other, especially
   with single-digit counts. */
.hour-bars-head, .hour-bar-row {
  display: grid; grid-template-columns: 48px 1fr 60px 76px; column-gap: 18px; align-items: center;
}
.hour-bars-head {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--muted); padding-bottom: 8px; margin-bottom: 8px;
  border-bottom: 1px solid var(--border);
}
.hour-bars-head .hour-bar-events, .hour-bars-head .hour-bar-avg { text-align: right; }
.hour-bars { display: flex; flex-direction: column; gap: 6px; }
.hour-bar-row { font-size: 12px; }
.hour-bar-label { color: var(--muted); text-align: right; font-variant-numeric: tabular-nums; }
.hour-bar-track { height: 14px; background: var(--surface); border-radius: 4px; overflow: hidden; }
/* the fill is a <span> - without an explicit display it stays 'inline',
   which makes width/height no-ops and the bar invisible regardless of
   what percentage gets set on it */
.hour-bar-fill { display: block; height: 100%; background: var(--blue); border-radius: 4px; min-width: 2px; }
.hour-bar-events { text-align: right; color: var(--text); font-variant-numeric: tabular-nums; }
.hour-bar-avg { text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }

/* overflow-x: auto (was plain `overflow: hidden`, which has no scrollbar of
   its own - anything wider than the wrapper just got silently clipped off
   the edge of the screen instead of being reachable at all) so a table too
   wide for the viewport - the List view's Name/Joined/Duration/IP/
   Browser-OS columns, Customer Stats' by-day columns, or a nested Past
   day's Timeline, all of which live inside this wrapper - can be scrolled
   into view exactly the way Today's Timeline already could (see
   .stats-timeline below, which had its own overflow-x:auto all along;
   this class just never got the same treatment). overflow-y stays clipped
   since border-radius still wants to round off the table's corners and
   nothing here is expected to overflow vertically. */
.admin-day-table-wrap {
  border: 1px solid var(--border); border-radius: 8px; background: var(--raised);
  overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
}
.admin-day-table-wrap .admin-day-table { margin: 0; }
.admin-day-table-wrap .admin-day-table th, .admin-day-table-wrap .admin-day-table td { padding: 10px 12px; }
.admin-day-table-nested th, .admin-day-table-nested td { padding: 6px 10px; }

.admin-ban-form { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.admin-ban-form input {
  flex: 1; min-width: 160px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text); font-size: 14px;
}

.btn-secondary {
  width: 100%; padding: 10px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text); font-weight: 600; font-size: 15px;
  cursor: pointer; margin-top: 8px;
}

#kickModalOverlay .modal, #loginScreen .modal { text-align: left; }
#kickModalOverlay .modal h2, #loginScreen .modal h2 { text-align: center; }

/* Same breakpoint style.css's main board already uses for its own mobile
   tightening. Tighter padding/font here means Customer Stats' by-day table
   (Date/Served/Left/Avg wait/Longest wait/Clear day - all short values) has
   a real chance of fitting a typical phone width without needing the
   horizontal scroll from .admin-day-table-wrap above at all. The List
   view's Name/Joined/Duration/IP/Browser-OS columns and a Past day's
   Timeline are wide enough (a full date+time stamp, a raw IP address, a
   24h axis) that no reasonable amount of tightening gets them under a
   phone's width - those still fall back to that same horizontal scroll,
   which is the acceptable outcome for content that just doesn't fit. */
@media (max-width: 480px) {
  .admin-main { padding: 14px 10px 60px; }
  .admin-day-table-wrap .admin-day-table th,
  .admin-day-table-wrap .admin-day-table td { padding: 8px 6px; }
  .admin-day-table { font-size: 12px; }
  .day-clear-btn, .hour-clear-btn { padding: 3px 6px; font-size: 11px; }
}

/* ---------- v4.0.0: manager badge + abandoned-row marker (admin-only) ---------- */
/* "(M)" on a presence pill - only ever present at all on the admin-only
   presence payload (see presenceListForAdmin in server.js), so this rule
   never has anything to style on the regular board. */
.manager-badge { font-size: 10px; font-weight: 700; opacity: 0.75; margin-left: 2px; }

/* An entry checkAbandonedRows has written off as a stale outlier - still a
   normal, fully-interactive row (nothing here disables it), just visually
   called out so it's obvious at a glance why it won't show up in Customer
   Stats. The ⊘ suffix on the timestamp itself (see admin.js's renderRow)
   carries the same signal for anyone scanning without hovering for the
   title tooltip. */
#liveRows .row.row-abandoned { opacity: 0.72; }
#liveRows .row.row-abandoned .row-timestamp { color: var(--red); font-weight: 600; }

/* ---------- v4.0.0: User management tab ---------- */
.admin-manager-form { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.admin-manager-form input {
  flex: 1; min-width: 160px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text); font-size: 14px;
}
/* Colour override swatch - available on every row in every section here
   (Managers, All users, Dormant users), not just managers. */
.user-color-swatch {
  width: 18px; height: 18px; border-radius: 50%; border: 1px solid var(--border);
  cursor: pointer; padding: 0; flex-shrink: 0;
}
.admin-list-item-main:has(.user-color-swatch) { flex-direction: row; align-items: center; gap: 8px; }
.admin-list-item-main:has(.user-color-swatch) .admin-list-sub { margin-left: 2px; }
/* Dormant users list starts collapsed (see admin.js's dormantExpanded) -
   the toggle button above it carries its own count/arrow as plain text,
   so no extra chevron styling is needed here. */
#dormantUsersList.hidden { display: none; }
/* Overrides .btn-secondary's own look (width:100%, bold, 15px, bordered
   surface) - that reads fine for an actual secondary action button (e.g.
   the Cancel buttons in the Kick/Ban modals), but way too heavy for what's
   really just a disclosure toggle for a collapsible list. */
#dormantToggleBtn {
  width: auto; text-align: left; margin: 4px 0 16px; padding: 6px 4px;
  border: none; background: transparent; font-weight: 500; font-size: 13px;
  color: var(--muted);
}
#dormantToggleBtn:hover { color: var(--text); }
