:root {
  /* "Kabs" theme - light surfaces throughout, deep-navy text, a single bright cyan
     accent - modeled after kabs.de's own palette (navy headings/nav, cyan buttons
     and links, white/light-gray surfaces, soft rounded cards). Every color/radius/
     shadow in this file reads from these variables (see the bottom of this file for
     the couple of animation/toast/switch rules added alongside the theme), so this
     block is the one place a future theme swap needs to touch. Replaces the previous
     dark "Midnight" theme end to end - see git history if that's ever needed again. */
  --bg: #f4f6fa;
  --surface: #ffffff;
  --surface-2: #eaf1f8;
  --border: #dde4ed;
  --border-hover: #b9c6d6;
  --text: #0b2a4a;
  --text-muted: #64748b;

  --accent: #1ea7e1;
  --accent-hover: #1690c6;
  --accent-soft: rgba(30, 167, 225, 0.12);
  --accent-soft-strong: rgba(30, 167, 225, 0.32);

  --danger: #dc2626;
  --danger-soft: rgba(220, 38, 38, 0.10);
  --danger-soft-strong: rgba(220, 38, 38, 0.30);
  --danger-text: #b91c1c;

  --warn-soft: rgba(217, 119, 6, 0.12);
  --warn-text: #b45309;

  --ok: #059669;
  --ok-soft: rgba(5, 150, 105, 0.10);
  --ok-text: #047857;

  --neutral-soft: rgba(100, 116, 139, 0.10);
  --neutral-text: #475569;

  /* This Jira site's three real organizations (see Organization.java: SPH, VISUCO, SMM) -
     colorblind-validated (dataviz skill's validator) originally for the Dashboard's own org
     filter chips/bars, and shared here at :root (rather than scoped under .dashboard-page the
     way they used to be) now that the Issues page's own org-filter chips (see .org-chip below)
     need the exact same three colors for the same three organizations. */
  --org-1: #1ea7e1; /* SPH */
  --org-2: #f59e0b; /* VISUCO */
  --org-3: #8b5cf6; /* SMM */

  /* The one surface that deliberately stays dark in this otherwise light theme -
     echoes kabs.de's own dark-navy utility bar, and gives the app a fixed visual
     anchor/brand column the way that site uses navy for its header chrome. */
  --sidebar-bg: #0b2340;
  --sidebar-hover: rgba(255, 255, 255, 0.08);
  --sidebar-active: rgba(30, 167, 225, 0.28);
  --sidebar-text: #9fb3c8;

  /* Topbar chrome - matches the look of "SNAK One" (a sibling internal SNAK Consultancy
     app): a solid royal-blue bar instead of this theme's usual white/light surfaces. */
  --topbar-bg: #2b57ab;

  --shadow-card: 0 1px 2px rgba(15, 35, 60, 0.04), 0 8px 20px rgba(15, 35, 60, 0.07);
  --shadow-pop: 0 20px 45px rgba(15, 35, 60, 0.18);

  --radius-card: 14px;
  --radius-btn: 9px;
  --radius-pill: 999px;

  /* kabs.de pairs a bold, rounded geometric display face for headings/buttons with a
     plain, highly-legible sans for body copy - Poppins/Inter are the closest freely
     licensed match to that pairing (both loaded via Google Fonts, see each page's
     <head>), with the same system-font stack kept as the fallback if either fails
     to load. */
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --font-heading: 'Poppins', var(--font-body);

  /* Tells the browser this page is light, so native controls that aren't styled by
     CSS below - the date-picker popup on <input type="date">, scrollbars in browsers
     that support styling them, form-control default chrome - render light instead of
     a jarring dark popup on top of a light page. */
  color-scheme: light;
}

input[type="checkbox"], input[type="radio"] {
  accent-color: var(--accent);
}

@keyframes fadeSlideRight { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: translateX(0); } }
@keyframes fadeSlideDown { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fadeSlideUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.5;
}

/* --- App shell: icon sidebar + topbar + content --- */

.app-shell {
  display: flex;
  align-items: stretch;
  min-height: 100vh;
}

.sidebar {
  flex: 0 0 84px;
  width: 84px;
  background: var(--sidebar-bg);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px 0;
  position: sticky;
  top: 0;
  height: 100vh;
  animation: fadeSlideRight 0.4s ease both;
  overflow: hidden;
  transition: flex-basis 0.2s ease, width 0.2s ease, padding 0.2s ease;
}

/* The topbar's hamburger button (.topbar-menu-btn) toggles this class on .app-shell so the
   icon rail can be tucked away when someone wants the extra width - collapsing to 0 rather
   than just hiding, so it doesn't leave a blank gap next to the content. */
.app-shell.sidebar-collapsed .sidebar {
  flex-basis: 0;
  width: 0;
  padding-left: 0;
  padding-right: 0;
}

/* The real SNAK icon mark (navy "S" + orange arc, cropped from the company's own logo -
   see static/img/snak-icon.png) reads best on a plain white tile, not the accent color the
   old single-letter placeholder sat on, so this is white regardless of the rest of the
   theme. object-fit: contain (see the img rule below) keeps the mark's own proportions
   instead of stretching it to fill the square. */
.sidebar-brand {
  width: 40px;
  height: 40px;
  border-radius: 11px;
  background: #fff;
  padding: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 26px;
  flex: none;
}

.sidebar-brand img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.sidebar-nav {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 100%;
}

.sidebar-link {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  width: 68px;
  padding: 10px 4px 9px;
  border-radius: 12px;
  color: var(--sidebar-text);
  text-decoration: none;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.18s ease, color 0.18s ease;
}

.sidebar-link:hover {
  background: var(--sidebar-hover);
  color: #fff;
}

.sidebar-link.active {
  background: var(--sidebar-active);
  color: #fff;
}

.sidebar-link.active::before {
  content: '';
  position: absolute;
  left: -9px;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 20px;
  border-radius: 3px;
  background: var(--accent);
  box-shadow: 0 0 10px var(--accent);
}

.sidebar-icon {
  width: 20px;
  height: 20px;
  flex: none;
}

.app-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.topbar {
  display: flex;
  align-items: stretch;
  padding: 0;
  background: var(--topbar-bg);
  border-bottom: 1px solid rgba(0, 0, 0, 0.14);
  position: sticky;
  top: 0;
  z-index: 5;
  animation: fadeSlideDown 0.4s ease both;
}

.topbar-bar {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 24px;
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

/* Toggles .sidebar-collapsed on .app-shell (see the sidebar rules above) - tucks the icon
   rail away for anyone who wants the extra width, rather than sitting there decorative. */
.topbar-menu-btn {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  border: none;
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex: none;
  transition: background 0.15s ease;
}

.topbar-menu-btn:hover {
  background: rgba(255, 255, 255, 0.26);
}

.topbar-page {
  font-weight: 600;
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 14px;
  flex: none;
}

/* Purely visual for now - there's no notifications feature behind it yet, so unlike the
   reference screenshot this deliberately doesn't show a fake unread badge. */
.topbar-bell {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: none;
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex: none;
  transition: background 0.15s ease;
}

.topbar-bell:hover {
  background: rgba(255, 255, 255, 0.26);
}

.topbar-user-pill {
  display: flex;
  align-items: center;
  gap: 10px;
  background: #fff;
  border-radius: var(--radius-pill);
  padding: 6px 14px 6px 6px;
  flex: none;
  min-width: 0;
}

.user-avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.82rem;
  flex: none;
}

.user-name {
  font-size: 0.88rem;
  color: var(--text);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 180px;
}

.user-role-badge {
  background: var(--sidebar-bg);
  color: #fff;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  flex: none;
}

.signout-form { margin: 0; }

button.signout-btn {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.45);
  color: #fff;
  border-radius: var(--radius-btn);
  padding: 7px 14px;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
  transition: background 0.15s ease, border-color 0.15s ease;
}

button.signout-btn:hover {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.75);
}

main {
  max-width: 720px;
  margin: 0 auto;
  /* Grows to fill any leftover height in .app-main (a flex column) so .app-footer always
     lands at the bottom of the viewport on a short page instead of sitting right under a
     half-empty card - the standard "sticky footer" layout trick - while a tall page just
     pushes it down and scrolls normally. */
  flex: 1 0 auto;
  padding: 36px 24px 48px;
  width: 100%;
  animation: fadeSlideUp 0.35s ease both;
}

.app-footer {
  flex: none;
  padding: 14px 32px;
  text-align: right;
  color: var(--text-muted);
  font-size: 0.8rem;
  border-top: 1px solid var(--border);
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  padding: 28px;
  margin-bottom: 20px;
  box-shadow: var(--shadow-card);
}

h1 {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 1.4rem;
  margin: 0 0 6px;
}

p.hint {
  color: var(--text-muted);
  margin: 0 0 22px;
  font-size: 0.92rem;
}

/* A tab's intro hint plus a related one-off action (e.g. WhatsApp's "Send test message") -
   the action sits top-right, aligned with the top of the hint text rather than vertically
   centered against its full (possibly multi-line) height, and drops below the hint on a
   narrow screen instead of squeezing it. */
.settings-panel-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px 20px;
  margin-bottom: 22px;
}

.settings-panel-head p.hint {
  margin: 0;
  flex: 1 1 420px;
  min-width: 0;
}

.settings-panel-head > button {
  flex: none;
  white-space: nowrap;
}

p.hint code {
  background: var(--neutral-soft);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 0.85em;
}

.dropzone {
  border: 2px dashed var(--border);
  border-radius: 12px;
  padding: 28px;
  text-align: center;
  margin-bottom: 18px;
  background: var(--bg);
}

input[type="file"] {
  font: inherit;
}

button.primary {
  background: var(--accent);
  color: white;
  border: none;
  border-radius: var(--radius-btn);
  padding: 11px 22px;
  font-size: 0.95rem;
  font-family: var(--font-heading);
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(30, 167, 225, 0.28);
  transition: background 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

button.primary:hover { background: var(--accent-hover); transform: translateY(-1px); box-shadow: 0 6px 18px rgba(30, 167, 225, 0.4); }
button.primary:active { transform: scale(0.97); }
button.primary:disabled { opacity: 0.6; cursor: default; transform: none; box-shadow: none; }

/* Success micro-interaction for buttons whose action stays on the page (no form
   submit/reload) - e.g. "Send test message" on the Settings page. Its own JS
   toggles this class briefly after a successful fetch(). Not used on real
   <form> submit buttons, since those navigate away almost immediately and the
   pulse would never actually be seen - the toast shown after reload covers
   those instead. Higher specificity than button.primary/button.secondary so it
   overrides either one regardless of which the button also carries. */
button.is-success, button.primary.is-success, button.secondary.is-success {
  background: var(--ok) !important;
  color: #fff !important;
  border-color: var(--ok) !important;
  box-shadow: 0 6px 18px rgba(52, 211, 153, 0.4);
  animation: btnPulse 0.4s ease;
}

@keyframes btnPulse {
  0% { transform: scale(1); }
  40% { transform: scale(1.05); }
  100% { transform: scale(1); }
}

.alert {
  border-radius: 10px;
  padding: 14px 16px;
  margin-bottom: 20px;
  font-size: 0.92rem;
  border: 1px solid transparent;
}

.alert.error {
  background: var(--danger-soft);
  color: var(--danger-text);
  border-color: var(--danger-soft-strong);
}

.alert.success {
  background: var(--ok-soft);
  color: var(--ok-text);
  border-color: rgba(52, 211, 153, 0.35);
}

.alert.warn {
  background: var(--warn-soft);
  color: var(--warn-text);
  border-color: rgba(251, 191, 36, 0.35);
}

.alert.info {
  background: var(--neutral-soft);
  color: var(--neutral-text);
  border-color: var(--border);
}

.stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  margin: 18px 0;
}

.stat {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  text-align: center;
}

.stat .n {
  font-size: 1.4rem;
  font-weight: 700;
  font-family: var(--font-heading);
}

.stat .l {
  font-size: 0.76rem;
  color: var(--text-muted);
  letter-spacing: 0.03em;
}

.row-errors {
  margin-top: 14px;
  max-height: 220px;
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 16px;
  font-family: "SFMono-Regular", Consolas, monospace;
  font-size: 0.8rem;
  color: var(--text-muted);
}

.row-errors div { margin-bottom: 6px; }

/* --- "Fetch tickets from Jira" form (upload page) --- */

.jira-fetch-form {
  display: flex;
  align-items: flex-end;
  gap: 16px;
  flex-wrap: wrap;
}

.jira-fetch-form .field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.jira-fetch-form label {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
}

.jira-fetch-form select,
.jira-fetch-form input[type="date"] {
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  font-size: 0.92rem;
  font-family: inherit;
  color: var(--text);
  background: var(--surface);
  min-width: 160px;
}

.jira-fetch-form button.primary {
  height: 42px;
}

/* --- Settings page (Jira / WhatsApp configuration forms) --- */

/*
 * Each tab's form is a responsive grid instead of one field per row down a
 * narrow column - short fields (a URL, an email, a numeric setting, ...) sit
 * two or three to a row on a normal-width screen, and auto-fit collapses
 * back to a single column under ~600px with no extra media query needed.
 * Anything that needs the row to itself instead - a switch, a checkbox row,
 * a field whose hint runs long (Cron expression, Recipient number(s)), the
 * permissions matrix, a trailing hint or the Save button - opts back into a
 * full-width row via the grid-column rule just below.
 */
.settings-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 20px 24px;
  max-width: 1040px;
  align-items: start;
}

.settings-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.settings-row-full,
.settings-row-checkbox,
.switch-row,
.settings-form > .table-wrap,
.settings-form > .settings-field-hint {
  grid-column: 1 / -1;
}

.settings-form > button {
  grid-column: 1 / -1;
  justify-self: start;
}

.settings-row label {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
}

.settings-row input[type="text"],
.settings-row input[type="password"],
.settings-row input[type="number"] {
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  font-size: 0.92rem;
  font-family: inherit;
  color: var(--text);
  background: var(--surface);
  width: 100%;
}

.settings-row input[type="text"]:focus,
.settings-row input[type="password"]:focus,
.settings-row input[type="number"]:focus {
  outline: none;
  border-color: var(--accent);
}

.settings-field-hint {
  font-size: 0.78rem;
  color: var(--text-muted);
}

.settings-field-hint code {
  background: var(--neutral-soft);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 0.9em;
}

.settings-row-checkbox {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 8px;
}

.settings-checkbox-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.88rem;
  font-weight: 500;
  color: var(--text);
  cursor: pointer;
}

.settings-checkbox-label input[type="checkbox"] {
  width: 16px;
  height: 16px;
}

.settings-row-checkbox .settings-field-hint {
  flex-basis: 100%;
}

.settings-test {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 22px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}

.settings-test .alert {
  margin-bottom: 0;
}

.settings-status-block {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 22px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
  font-size: 0.88rem;
  color: var(--text);
}

.settings-status-block .pill {
  margin-left: 6px;
}

/* --- "Access control" card: sub-section headings (admins list vs. permission checkboxes) --- */

.settings-subheading {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
  margin: 24px 0 4px;
  font-family: var(--font-heading);
}

.settings-subheading:first-of-type {
  margin-top: 4px;
}

/* --- Settings page tabs (replaces the old single-open accordion) --- */

.settings-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
}

.settings-tab {
  flex: none;
  padding: 12px 18px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-family: var(--font-heading);
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.settings-tab:hover {
  color: var(--text);
}

.settings-tab.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.settings-tab-card {
  padding-top: 8px;
}

.settings-tab-panel {
  display: none;
}

.settings-tab-panel.active {
  display: block;
  animation: fadeSlideUp 0.25s ease both;
}

/* --- "Access control" tab: the Read/Write-by-role matrix table --- */

table.matrix-table th, table.matrix-table td {
  text-align: center;
}

table.matrix-table thead th {
  cursor: default;
}

table.matrix-table th:first-child, table.matrix-table td:first-child {
  text-align: left;
}

.matrix-role-head {
  border-left: 1px solid var(--border);
}

.matrix-sub-head {
  font-size: 0.7rem !important;
}

.matrix-cell input[type="checkbox"] {
  width: 17px;
  height: 17px;
  cursor: pointer;
}

.matrix-cell-na {
  color: var(--text-muted);
}

/* --- "Reports data" card: one purge row per report --- */

.settings-purge-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px 20px;
  padding: 16px 0;
  border-top: 1px solid var(--border);
  max-width: 900px;
}

.settings-purge-row:first-of-type {
  border-top: none;
  padding-top: 6px;
}

.settings-purge-info {
  flex: 1 1 260px;
  min-width: 0;
}

.settings-purge-title {
  font-weight: 700;
  font-size: 0.92rem;
  color: var(--text);
  margin-bottom: 4px;
}

.settings-purge-action {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 0 0 auto;
  width: 260px;
}

.purge-confirm-input {
  padding: 9px 11px;
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  font-size: 0.82rem;
  font-family: inherit;
  color: var(--text);
  background: var(--surface);
  width: 100%;
}

.purge-confirm-input:focus {
  outline: none;
  border-color: var(--danger-soft-strong);
}

.settings-purge-action button.delete-btn {
  width: 100%;
  padding: 9px 12px;
}

/* --- Settings page accordion (one section open at a time) --- */

.accordion-item {
  padding: 0;
  overflow: hidden;
}

.accordion-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 28px;
  border: none;
  background: transparent;
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  color: var(--text);
  transition: background 0.15s ease;
}

.accordion-header:hover {
  background: var(--neutral-soft);
}

.accordion-title {
  font-size: 1.4rem;
  font-weight: 700;
  font-family: var(--font-heading);
}

.accordion-chevron {
  flex: none;
  color: var(--text-muted);
  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), color 0.15s ease;
}

.accordion-header[aria-expanded="true"] .accordion-chevron {
  transform: rotate(-180deg);
  color: var(--accent);
}

/* Smooth expand/collapse without JS measuring scrollHeight: animating a grid track
   from 0fr to 1fr auto-sizes to the panel's real (possibly changing) content height,
   unlike a max-height transition which needs a guessed/hardcoded ceiling. Requires
   .accordion-panel-inner for the overflow:hidden + padding (see settings.html) - a
   0fr row still fully collapses even though its child wants its natural height. */
.accordion-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}

.accordion-panel.open {
  grid-template-rows: 1fr;
}

.accordion-panel-inner {
  overflow: hidden;
  padding: 0 28px 28px;
}

/* --- Organization multi-select (Jira fetch form) --- */

.org-multiselect {
  position: relative;
}

.multiselect-wrap {
  position: relative;
}

button.multiselect-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-width: 200px;
  height: 42px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  background: var(--surface);
  color: var(--text);
  font-size: 0.92rem;
  font-family: inherit;
  cursor: pointer;
}

button.multiselect-toggle:hover {
  border-color: var(--border-hover);
}

button.multiselect-toggle .chevron {
  flex: none;
  color: var(--text-muted);
  transition: transform 0.15s ease;
}

button.multiselect-toggle[aria-expanded="true"] .chevron {
  transform: rotate(180deg);
}

.multiselect-panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 20;
  min-width: 220px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-pop);
  padding: 8px;
}

/* Same rationale as .filter-panel[hidden] / .modal-overlay[hidden] above: bumps
   specificity so the hidden-state rule reliably wins over any author rule that
   might set display on .multiselect-panel. */
.multiselect-panel[hidden] {
  display: none;
}

.multiselect-option {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px;
  border-radius: 7px;
  font-size: 0.9rem;
  cursor: pointer;
  user-select: none;
}

.multiselect-option:hover {
  background: var(--neutral-soft);
}

.multiselect-option input {
  margin: 0;
  accent-color: var(--accent);
}

.multiselect-option-all {
  font-weight: 700;
  border-bottom: 1px solid var(--border);
  border-radius: 7px 7px 0 0;
  margin-bottom: 4px;
  padding-bottom: 10px;
}

.field-error {
  color: var(--danger-text);
  font-size: 0.78rem;
  margin-top: 6px;
}

/* --- Issues listing page --- */

/* Listing/data pages (Issues, History, WhatsApp History, Settings) should use the full
   width available next to the sidebar on a big monitor instead of capping out and leaving
   the rest of the screen empty - unlike the narrow single-column .card pages (Upload,
   access-denied) where a hard cap keeps short forms readable, these pages are already
   table/grid-based and get more useful on a wide screen, not less. */
main.wide {
  max-width: none;
}

.list-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.search-box {
  flex: 1;
  min-width: 220px;
  position: relative;
}

.search-box .search-icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted);
  pointer-events: none;
}

.search-box input {
  width: 100%;
  padding: 10px 14px 10px 38px;
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  font-size: 0.92rem;
  font-family: inherit;
  background: var(--surface);
}

.search-box input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.list-toolbar .right {
  display: flex;
  align-items: center;
  gap: 12px;
}

.row-count {
  color: var(--text-muted);
  font-size: 0.85rem;
  white-space: nowrap;
}

button.secondary {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  padding: 9px 16px;
  font-size: 0.88rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  font-family: var(--font-heading);
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

button.secondary:hover {
  background: var(--neutral-soft);
  border-color: var(--border-hover);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(15, 35, 60, 0.08);
}

button.secondary:active {
  transform: scale(0.97);
}

/* --- Advanced filter panel (Organization / Created date range / Severity) --- */

.filter-dropdown-wrap {
  position: relative;
}

button.filter-toggle-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

button.filter-toggle-btn.active {
  background: var(--accent-soft);
  border-color: var(--accent-soft-strong);
  color: var(--accent);
}

.filter-badge {
  background: var(--accent);
  color: white;
  font-family: var(--font-heading);
  border-radius: var(--radius-pill);
  min-width: 17px;
  height: 17px;
  padding: 0 5px;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
}

.filter-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 20;
  width: 300px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-pop);
  padding: 18px;
}

/* Same rationale as .modal-overlay[hidden] below: without this, an author-stylesheet
   rule that sets display on .filter-panel would beat the browser's default
   "[hidden] { display: none }" on specificity ties. Here .filter-panel never sets
   display itself, so this isn't strictly load-bearing yet - but it's kept for
   safety since the panel is show/hidden purely via the hidden attribute. */
.filter-panel[hidden] {
  display: none;
}

.filter-panel-section {
  margin-bottom: 16px;
}

.filter-panel-section:last-of-type {
  margin-bottom: 12px;
}

.filter-panel-label {
  font-size: 0.76rem;
  font-weight: 700;
  color: var(--text-muted);
  letter-spacing: 0.04em;
  margin-bottom: 8px;
}

.filter-chip-group {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 6px 12px 6px 10px;
  font-size: 0.85rem;
  cursor: pointer;
  user-select: none;
}

/* :has() covers modern browsers; .filter-chip.checked (toggled in JS on the
   checkbox's change event) covers everything else, so the highlight works
   either way. */
.filter-chip:has(input:checked),
.filter-chip.checked {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

.filter-chip input {
  margin: 0;
  accent-color: var(--accent);
}

.filter-empty-note {
  color: var(--text-muted);
  font-size: 0.82rem;
}

.filter-daterange {
  display: flex;
  align-items: center;
  gap: 8px;
}

.filter-daterange input[type="date"] {
  flex: 1;
  min-width: 0;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 0.85rem;
  font-family: inherit;
  color: var(--text);
  background: var(--surface);
}

.filter-daterange-sep {
  color: var(--text-muted);
  font-size: 0.82rem;
}

#filterSeverity {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 0.88rem;
  font-family: inherit;
  color: var(--text);
  background: var(--surface);
}

.filter-panel-actions {
  border-top: 1px solid var(--border);
  padding-top: 12px;
  text-align: right;
}

button.link-btn {
  background: none;
  border: none;
  color: var(--accent);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  padding: 0;
  text-decoration: underline;
  font-family: inherit;
}

.table-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow-x: auto;
  box-shadow: var(--shadow-card);
}

/* --- Pagination (shared by every listing page - Issues, History, WhatsApp History; give
   any future listing page the same #pagination markup + PAGE_SIZE/renderPage() JS pattern
   to pick this styling up for free) --- */

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: 16px;
}

.pagination-status {
  font-size: 0.85rem;
  color: var(--text-muted);
  white-space: nowrap;
  min-width: 90px;
  text-align: center;
}

/*
 * min-width, NOT width: this table lives inside .table-wrap (overflow-x: auto). A flat
 * "width: 100%" would cap the table AT exactly the wrapper's width no matter how many
 * columns it has, forcing the browser to compress every column and wrap text inside them
 * instead of growing past the wrapper and letting it scroll horizontally - on a table with
 * this many columns (see issues.html's 13 data columns + Actions), that compression is what
 * makes several columns wrap onto multiple lines at once, crowding their text together
 * until it reads as overlapping/garbled. min-width: 100% keeps the same "fill the card when
 * there's room" look when content is narrow (Access control's much shorter matrix table)
 * while still letting the table grow past 100% - and the wrapper scroll - when it needs to.
 */
table.data {
  min-width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}

table.data th, table.data td {
  padding: 10px 14px;
  text-align: left;
  /* Browsers default table cells to vertical-align: middle. A row with one long-wrapping
     cell (a long Title/Request Type) next to several short single-line cells would then
     center those short cells partway down the tall row instead of lining them up with the
     wrapped cell's first line - part of what made a wrapped row read as jumbled/overlapping
     alongside the width fix above. */
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}

/*
 * Every data cell across every listing (Issues, History, WhatsApp History, ...) renders as
 * a single line with an ellipsis instead of wrapping a long value (a lengthy issue title, a
 * "Jira fetch: ..." history source description, a comma-separated WhatsApp recipient list,
 * ...) across several lines - keeps every row a uniform height no matter how long one
 * cell's text is. The Actions column is excluded: it holds buttons, not text, and already
 * sizes itself to content via width: 1% (see td.action-col below). The full value is still
 * available - each listing's script sets a native title tooltip (see attachCellTooltips())
 * on any cell this actually clips, so hovering it reveals the complete text.
 */
table.data td:not(.action-col) {
  max-width: 280px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

table.data thead th {
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: 0.76rem;
  /* Column headers are already written in Title Case in the markup (Sent At, Issue Key,
     Organization, ...) - this used to force them to full caps regardless, hiding that. */
  letter-spacing: 0.04em;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

table.data thead th:hover {
  color: var(--text);
}

table.data thead th .arrow {
  display: inline-block;
  width: 12px;
  opacity: 0.4;
}

table.data thead th.sorted .arrow {
  opacity: 1;
}


table.data tbody tr {
  transition: background 0.15s ease;
}

/*
 * The hover accent bar lives on the row's first real cell, NOT on the <tr> itself. A <tr>
 * (display: table-row) requires every direct child box to be a table-cell; a ::before with
 * ordinary generated content doesn't qualify, so browsers silently wrap it in its own
 * anonymous table-cell to satisfy that rule - which consumes a real column slot and pushes
 * every actual <td> in the row one column to the right. That's what caused the Module
 * column's header and the whole Read/Write matrix to visibly misalign. A <td> is already a
 * valid table-cell, so a ::before inside one never triggers this. The negative left/top/
 * bottom offsets below just cancel that cell's own padding so the bar still reaches the
 * table's true left edge and the row's full height, matching how it looked before this fix.
 */
table.data tbody tr td:first-child {
  position: relative;
}

table.data tbody tr td:first-child::before {
  content: '';
  position: absolute;
  left: -14px;
  top: -10px;
  bottom: -10px;
  width: 3px;
  background: var(--accent);
  transform: scaleY(0);
  transition: transform 0.18s ease;
}

table.data tbody tr:hover {
  background: rgba(15, 35, 60, 0.035);
}

table.data tbody tr:hover td:first-child::before {
  transform: scaleY(1);
}

/* /issues row-level warning: the whole row (not just a Due Date cell) tints light red once a
   ticket's due date is today, tomorrow, or already past - see IssueListRow#isDueUrgent's own
   Javadoc for exactly what counts. Layered on top of (not instead of) the plain hover tint above
   so a hover is still visible on an already-highlighted row. */
table.data tbody tr.row-due-urgent {
  background: var(--danger-soft);
}

table.data tbody tr.row-due-urgent:hover {
  /* --danger-soft-strong (0.30 opacity) is tuned as a border/outline tone elsewhere, not a
     full-row fill - at that opacity here it would obscure the row's own text, so this row's
     hover state gets its own, gentler step up from --danger-soft's 0.10 instead. */
  background: rgba(220, 38, 38, 0.16);
}

/* Delete confirmation (issues.html): fades/collapses a row out instead of yanking it
   from the table instantly - see removeRowAnimated() there. */
table.data tbody tr.row-removing {
  transition: opacity 0.25s ease, transform 0.25s ease;
  opacity: 0;
  transform: scale(0.98);
  pointer-events: none;
}

td.key-col {
  font-family: "SFMono-Regular", Consolas, monospace;
  color: var(--accent);
  font-weight: 600;
}

td.key-col a {
  color: inherit;
  text-decoration: none;
}

td.key-col a:hover {
  text-decoration: underline;
}

.empty-state {
  padding: 40px;
  text-align: center;
  color: var(--text-muted);
}

/* --- Status/priority pill badges (issues table) --- */

.pill {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.3;
  white-space: nowrap;
}

.pill-danger { background: var(--danger-soft); color: var(--danger-text); }
.pill-warn { background: var(--warn-soft); color: var(--warn-text); }
.pill-ok { background: var(--ok-soft); color: var(--ok-text); }
.pill-neutral { background: var(--neutral-soft); color: var(--neutral-text); }

/* --- Organization / Time Range quick-filter chips - originally the Dashboard's own top-of-page
   controls, now shared with the /issues page too (see issues.html's own org-filter-row/
   period-filter-row markup, alongside its existing Organization/Created-date-range/Severity
   controls in the Filters dropdown rather than replacing them). Moved here from dashboard.css
   (which used to own these rules outright) so both pages read one definition. --- */

.org-filter-row { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px; margin-bottom: 10px; }
.org-filter { display: flex; flex-wrap: wrap; gap: 8px; }
.org-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 14px; border-radius: var(--radius-pill);
  border: 1px solid var(--border); background: var(--surface);
  font-family: var(--font-body); font-size: .8rem; font-weight: 600; color: var(--text-muted);
  cursor: pointer; transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.org-chip .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.org-chip:hover { border-color: var(--accent); color: var(--text); }
.org-chip.active { background: var(--sidebar-bg); border-color: var(--sidebar-bg); color: #fff; }
.filter-scope { margin-left: auto; font-size: .8rem; color: var(--text-muted); }
.filter-scope b { color: var(--text); }

.period-filter-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
.period-label { font-size: .8rem; font-weight: 600; color: var(--text-muted); }
.period-filter { display: flex; gap: 4px; background: var(--surface-2); padding: 4px; border-radius: var(--radius-pill); }
.period-chip {
  border: none; background: transparent; padding: 6px 14px; border-radius: var(--radius-pill);
  font-family: var(--font-body); font-size: .8rem; font-weight: 600; color: var(--text-muted); cursor: pointer;
  transition: background .15s ease, color .15s ease, box-shadow .15s ease;
}
.period-chip:hover { color: var(--text); }
.period-chip.active { background: var(--surface); color: var(--text); box-shadow: 0 1px 3px rgba(15, 35, 60, .18); }

/* /issues page only: Organization and Time Range share one row instead of the Dashboard's own
   two stacked rows - org chips left-aligned, Time Range pushed to the right edge via
   margin-left: auto on its wrapper. */
.quick-filter-row { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px; margin-bottom: 14px; }
.period-filter-inline { display: flex; align-items: center; gap: 10px; margin-left: auto; flex-wrap: wrap; }

/* --- Summary count chips (/issues page, above the table) - live counts over whatever the
   search box + Filters panel currently narrow the table to (see issues.html's
   renderSummaryChips()), the lightest-weight of the count-card designs reviewed for this page
   ("Option C" of that design pass) so the table itself stays the visual focus. */
.count-chip-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 14px 0; }
.count-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px 5px 10px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text);
}
.count-chip .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.count-chip b { font-family: var(--font-heading); font-weight: 700; margin-left: 2px; }
.count-chip.danger { background: var(--danger-soft); color: var(--danger-text); }
.count-chip.warn { background: var(--warn-soft); color: var(--warn-text); }
.count-chip.ok { background: var(--ok-soft); color: var(--ok-text); }

/* The Due Date chip is the only one with a rule worth spelling out (which exact dates count as
   "urgent") - a native title tooltip on the whole chip covers "hover to see the rule" without a
   whole custom tooltip component; the small ⓘ just signals that hovering does something. */
#chipDueDate { cursor: help; }
#chipDueDate .info-icon { opacity: 0.7; font-size: 0.82rem; line-height: 1; }

/* --- Active Filters row (/issues page, above the count chips) - a live summary of every
   filter mechanism currently narrowing the table (Organization/Time Range quick chips, Status
   checkboxes, Severity, Created Date Range, search box - see issues.html's
   renderActiveFilters()), each rendered as its own "Label: value" pill with a small × to clear
   just that one filter. Hidden entirely (via the [hidden] attribute toggled in JS) when nothing
   is active, so it never adds visual weight on the unfiltered default view. */
.active-filters-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.active-filters-row .active-filters-label { font-size: 0.76rem; font-weight: 600; color: var(--text-muted); margin-right: 2px; }
.active-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  color: var(--text);
  font-size: 0.78rem;
  font-weight: 500;
}
.active-filter-chip b { font-family: var(--font-heading); font-weight: 700; margin-left: 3px; }
.active-filter-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--text-muted);
  font-size: 0.82rem;
  line-height: 1;
  cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.active-filter-x:hover { background: rgba(15, 35, 60, .12); color: var(--danger-text); }

/* --- Analyse column + button --- */

th.col-action {
  cursor: default;
  width: 1%;
  white-space: nowrap;
}

td.action-col {
  width: 1%;
  white-space: nowrap;
}

.action-buttons {
  display: flex;
  gap: 6px;
}

button.analyze-btn {
  background: var(--accent-soft);
  color: var(--accent);
  border: 1px solid var(--accent-soft-strong);
  border-radius: 7px;
  padding: 6px 12px;
  font-size: 0.8rem;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  font-family: var(--font-heading);
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

button.analyze-btn:hover {
  background: var(--accent);
  color: white;
  border-color: var(--accent);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(30, 167, 225, 0.3);
}

/* Also doubles as the "Delete" confirm button inside #deleteConfirmOverlay - same
   visual language as analyze-btn, danger palette instead of accent. */
button.delete-btn {
  background: var(--danger-soft);
  color: var(--danger-text);
  border: 1px solid var(--danger-soft-strong);
  border-radius: 7px;
  padding: 6px 12px;
  font-size: 0.8rem;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  font-family: var(--font-heading);
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

button.delete-btn:hover {
  background: var(--danger);
  color: white;
  border-color: var(--danger);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(220, 38, 38, 0.28);
}

button.delete-btn:disabled {
  opacity: 0.6;
  cursor: default;
  transform: none;
}

/* --- History page: "View" errors button (same visual language as Analyse) --- */

button.view-errors-btn {
  background: var(--accent-soft);
  color: var(--accent);
  border: 1px solid var(--accent-soft-strong);
  border-radius: 7px;
  padding: 6px 12px;
  font-size: 0.8rem;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  font-family: var(--font-heading);
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

button.view-errors-btn:hover {
  background: var(--accent);
  color: white;
  border-color: var(--accent);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(30, 167, 225, 0.3);
}

button.view-calls-btn {
  background: var(--accent-soft);
  color: var(--accent);
  border: 1px solid var(--accent-soft-strong);
  border-radius: 7px;
  padding: 6px 12px;
  font-size: 0.8rem;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
  font-family: var(--font-heading);
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

button.view-calls-btn:hover {
  background: var(--accent);
  color: white;
  border-color: var(--accent);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(30, 167, 225, 0.3);
}

/* --- History page: "API calls" popup contents --- */

/* Two classes (not just source order) so this reliably wins over the plain .modal
   rule below regardless of which one happens to come later in the stylesheet. */
.modal.modal-wide {
  max-width: 860px;
}

/* The "Delete issue" confirm dialog - a short message and two buttons don't need
   .modal's full 640px default width. */
.modal.modal-narrow {
  max-width: 420px;
}

.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 18px;
}

.api-call-card {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
  margin-bottom: 14px;
}

.api-call-card:last-child {
  margin-bottom: 0;
}

.api-call-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
  flex-wrap: wrap;
}

.api-call-seq {
  font-weight: 700;
  font-size: 0.85rem;
}

.api-call-time {
  color: var(--text-muted);
  font-size: 0.8rem;
}

.api-call-jql {
  font-size: 0.85rem;
  color: var(--text);
  margin-bottom: 10px;
  word-break: break-word;
}

.api-call-response {
  margin: 0;
  max-height: 320px;
  overflow: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  font-family: "SFMono-Regular", Consolas, monospace;
  font-size: 0.78rem;
  color: var(--text);
  white-space: pre;
}

/* --- Analysis popup --- */

body.modal-open {
  overflow: hidden;
}

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(10, 14, 20, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  z-index: 1000;
}

/* The [hidden] attribute is how the JS shows/hides this overlay (overlay.hidden
   = true/false). Without this rule, ".modal-overlay { display: flex }" above
   would win over the browser's default "[hidden] { display: none }" (same
   specificity, same author stylesheet), leaving the popup visible at all
   times instead of only after a click. This bumps the hidden-state rule's
   specificity so it wins instead. */
.modal-overlay[hidden] {
  display: none;
}

.modal {
  background: var(--surface);
  border-radius: var(--radius-card);
  width: 100%;
  max-width: 640px;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 20px 60px rgba(15, 35, 60, 0.22);
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 22px;
  border-bottom: 1px solid var(--border);
}

.modal-header h2 {
  margin: 0;
  font-size: 1.15rem;
}

.modal-close {
  background: none;
  border: none;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--text-muted);
  cursor: pointer;
  padding: 4px;
}

.modal-close:hover {
  color: var(--text);
}

.modal-body {
  padding: 20px 22px 26px;
  overflow-y: auto;
}

.modal-loading {
  color: var(--text-muted);
  font-size: 0.92rem;
  padding: 20px 0;
  text-align: center;
}

.modal-error {
  background: var(--danger-soft);
  color: var(--danger-text);
  border: 1px solid var(--danger-soft-strong);
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 0.9rem;
}

.a-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
}

.a-chip {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 5px 12px;
  font-size: 0.8rem;
  color: var(--text);
}

.a-chip-label {
  color: var(--text-muted);
  margin-right: 5px;
}

.a-summary {
  font-size: 0.95rem;
  font-weight: 600;
  margin: 0 0 16px;
}

.a-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  margin-bottom: 14px;
}

.a-stat {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 8px;
  text-align: center;
}

.a-stat-n {
  font-size: 1.25rem;
  font-weight: 700;
  font-family: var(--font-heading);
}

.a-stat-l {
  font-size: 0.72rem;
  color: var(--text-muted);
  letter-spacing: 0.03em;
}

.a-flag {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 8px 12px;
  font-size: 0.85rem;
  margin-bottom: 14px;
}

.a-flag-danger {
  background: var(--danger-soft);
  color: var(--danger-text);
  border-color: var(--danger-soft-strong);
  font-weight: 600;
}

.modal-body h3 {
  font-size: 0.85rem;
  letter-spacing: 0.03em;
  color: var(--text-muted);
  margin: 18px 0 8px;
}

.a-verdict {
  font-size: 0.92rem;
  margin: 0;
  line-height: 1.55;
}

.a-gaps, .a-timeline {
  list-style: none;
  margin: 0;
  padding: 0;
}

.a-gaps li {
  font-size: 0.88rem;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
}

.a-gaps li:last-child {
  border-bottom: none;
}

.a-timeline li {
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}

.a-timeline li:last-child {
  border-bottom: none;
}

.a-timeline-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  font-size: 0.88rem;
  margin-bottom: 3px;
}

.a-timeline-date {
  color: var(--text-muted);
  font-size: 0.78rem;
  white-space: nowrap;
}

.a-timeline-excerpt {
  color: var(--text-muted);
  font-size: 0.85rem;
  line-height: 1.4;
}

/* --- Animated toggle switch (e.g. "Enable WhatsApp priority alerts") --- */

.switch-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 16px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  margin-bottom: 4px;
}

.switch-row-label {
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text);
}

.switch-row .settings-field-hint {
  margin-top: 2px;
}

.switch {
  position: relative;
  display: inline-block;
  width: 42px;
  height: 24px;
  flex: none;
}

.switch input {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
}

.switch .track {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: var(--border-hover);
  transition: background 0.25s ease;
  cursor: pointer;
}

.switch .track::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(15, 35, 60, 0.35);
  transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.switch input:checked + .track {
  background: var(--accent);
  box-shadow: 0 0 12px rgba(30, 167, 225, 0.5);
}

.switch input:checked + .track::after {
  transform: translateX(18px);
}

.switch input:focus-visible + .track {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* --- Flash notifications: animated toasts (see static/js/toast.js) --- */

/* toast.js creates this on first use if it isn't already on the page, so no
   template has to include it - fixed positioning means where it sits in the
   DOM doesn't matter. */
.toast-viewport {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 2000;
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 340px;
  max-width: calc(100vw - 40px);
  pointer-events: none;
}

.toast {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  padding: 13px 34px 15px 14px;
  box-shadow: var(--shadow-pop);
  font-size: 0.88rem;
  color: var(--text);
  overflow: hidden;
  pointer-events: auto;
  animation: toastIn 0.35s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

.toast.toast-leaving {
  animation: toastOut 0.22s ease forwards;
}

.toast-icon {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 1px;
}

.toast-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

.toast-message {
  flex: 1;
  line-height: 1.4;
}

.toast-close {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 22px;
  height: 22px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 1.15rem;
  line-height: 1;
  cursor: pointer;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s ease, color 0.15s ease;
}

.toast-close:hover {
  color: var(--text);
  background: var(--neutral-soft);
}

.toast-bar {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  background: currentColor;
  opacity: 0.5;
  animation-name: toastShrink;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}

.toast.success { color: var(--ok); }
.toast.error   { color: var(--danger); }
.toast.warn    { color: var(--warn-text); }
.toast.info    { color: var(--text-muted); }

.toast.success { border-color: rgba(52, 211, 153, 0.35); }
.toast.error   { border-color: rgba(248, 113, 113, 0.4); }
.toast.warn    { border-color: rgba(251, 191, 36, 0.4); }
.toast.info    { border-color: var(--border); }

.toast .toast-message { color: var(--text); }

@keyframes toastIn {
  from { opacity: 0; transform: translateX(24px) scale(0.98); }
  to   { opacity: 1; transform: translateX(0) scale(1); }
}

@keyframes toastOut {
  from { opacity: 1; transform: translateX(0); }
  to   { opacity: 0; transform: translateX(24px); }
}

@keyframes toastShrink {
  from { width: 100%; }
  to   { width: 0%; }
}

/* The new topbar (hamburger + title + bell + user pill + sign out) packs in more fixed-width
   pieces than the old plain breadcrumb did - past this width there isn't room for all of
   them, so the least essential ones (the role badge, the visible user name) drop out and the
   rest tighten up. The avatar initial and Sign out button stay - those are the two things
   someone on a phone actually needs. */
@media (max-width: 720px) {
  .topbar-bar { padding: 10px 14px; gap: 8px; }
  .topbar-right { gap: 8px; }
  .user-name { display: none; }
  .user-role-badge { display: none; }
  .topbar-user-pill { padding: 4px; }
  button.signout-btn { padding: 7px 10px; }
}
