/* Local aliases — tied to the global theme tokens (assets/theme.css)
   so the entire stylesheet themes automatically when html.theme-light
   is set. Fallback hex preserved for non-themed contexts. */
:root{
  --bg:     var(--color-bg, #0d1218);
  --card:   var(--color-bg-elevated, #121a23);
  --text:   var(--color-text, #f3f6f8);
  --muted:  var(--color-text-muted, #9fb3c8);
  --border: var(--color-border-strong, #1f2a36);
  --green:#2e8b57; --green-bg:#1f5131; --yg:#c2a60d; --yg-bg:#5a4a1e;
  --blue:#3b82f6; --blue-hover:#2563eb;
  --blue-bg:#1b3d5a; --red:#ef4444; --red-bg:#5a1b1b; --gray:#5b7087; --gray-bg:#2b3541;
}
/* Light-theme overrides for the custom severity hues — dark bgs would
   be invisible on a white page. */
html.theme-light {
  --green-bg: #d8f0e0;
  --yg-bg:    #fff4d4;
  --blue-bg:  #dceaf9;
  --red-bg:   #fde6e9;
  --gray-bg:  #e4e8ee;
  --green:    #1e7a3d;
  --yg:       #8a6b00;
  --blue:     #1e63cc;
  --red:      #c92a47;
  --gray:     #4b5568;
}
html,body{background:var(--bg);color:var(--text);}
/* Fade transitions */
html, body { height: 100%; }
/* Body fade — disabled during light theme to avoid the dark-flash
   that happens between page nav and the theme class being applied.
   Dark theme keeps the fade because the unstyled body bg matches. */
body{opacity:0; transition: opacity .25s ease-in-out;}
body.fade-in{opacity:1;} body.fade-out{opacity:0;}
/* Light theme: skip the fade entirely. No more dark flash. */
html.theme-light body { opacity: 1 !important; transition: none !important; }
a{color:var(--text);text-decoration:none;} a:hover{text-decoration:underline;}
.header-bar{background:var(--color-bg-elevated, #151b23);}
.card-dark{background:var(--card);border:1px solid var(--border);} 
.hero{ background: linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0.02)); border:1px solid #1e2835; border-radius:14px; }
.hero .banner{ color:#cbd5e1; font-weight:700; text-align:center; opacity:.9; }
.hero .form-wrap{ display:flex; gap:12px; justify-content:center; }
.hero .form-control{ height:48px; max-width:720px; background:#ffffff; color:#111827; border:1px solid #d1d5db; border-radius:12px; padding:10px 14px; }
.hero .form-control::placeholder{ color:#9ca3af; }
.hero .btn-scan{ height:48px; padding:0 28px; border-radius:12px; background:var(--blue); border:1px solid rgba(255,255,255,.08); color:white; font-weight:600; }
.hero .btn-scan:hover{ background:var(--blue-hover); }
.hero .inner{ padding:22px; }
@media (max-width: 768px){
  .hero .form-wrap{ flex-direction:column; }
  .hero .form-control, .hero .btn-scan{ width:100%; }
}
table.table-dark{--bs-table-bg:var(--card);--bs-table-striped-bg:#0f1620;--bs-table-hover-bg:#1a2532;color:var(--text);}
.tag{display:inline-flex;align-items:center;padding:.2rem .6rem;border-radius:999px;font-weight:700;font-size:.85rem;width:fit-content;max-width:100%;}
.status-cell { white-space:nowrap; }
/* Wrapper around the status-cell pills (primary + optional "↻ scanning" /
   "⚠ last failed" secondary). Keeps both pills as a single child of the
   mobile <td> grid layout so they share one row instead of auto-flowing
   into two grid rows. flex-wrap lets them drop to a second LINE inside the
   value cell on very narrow viewports rather than overflowing. */
.status-pills { display:inline-flex; gap:6px; align-items:center; flex-wrap:wrap; }
.tag-green{background:var(--green-bg);border:1px solid var(--green);} .tag-yellow{background:var(--yg-bg);border:1px solid var(--yg);}
.tag-blue{background:var(--blue-bg);border:1px solid #0ea5e9;} .tag-red{background:var(--red-bg);border:1px solid var(--red);}
.tabs{display:flex;gap:8px;overflow-x:auto;padding-bottom:6px;} .tabs::-webkit-scrollbar{display:none;}
.tab{all:unset;border:1px solid #2c3643;border-radius:999px;padding:8px 14px;cursor:pointer;white-space:nowrap;}
.tab.active.gray{background:var(--gray-bg);border-color:var(--gray);color:#fff;}
.tab.active.blue{background:#103d63;border-color:#0ea5e9;color:#fff;}
.tab.active.yellow{background:#403513;border-color:#c2a60d;color:#fff;}
.tab.active.green{background:#103822;border-color:#2e8b57;color:#fff;}
.tab.active.red{background:#431717;border-color:#ef4444;color:#fff;}
@media (max-width:768px){
  #scanTable thead{display:none;}
  #scanTable tbody tr{display:block;margin:12px 0;border:1px solid var(--border);border-radius:12px;background:var(--card);}
  #scanTable tbody td{display:grid;grid-template-columns:92px 1fr;gap:10px;padding:10px 12px;border:none!important;min-width:0;}
  #scanTable tbody td::before{content:attr(data-label);font-weight:700;color:#93a1b3;}
  .text-end{text-align:left!important;}
  .url-cell a, .url-cell span { display:inline; white-space:normal; word-break:break-word; overflow-wrap:anywhere; }
  .status-cell { white-space:normal; }
}

/* Rounded scan list and nowrap date column */
.table-rounded { border-radius: 14px; overflow: hidden; }
.time-cell .text-nowrap { white-space: nowrap !important; }


/* URL can wrap, other columns stay in one line */
#scanTable td.url-cell { white-space: normal !important; word-break: break-word; }
#scanTable td:not(.url-cell) { white-space: nowrap; }
/* Keep header labels on one line — .table-responsive scrolls horizontally when the viewport is tight */
#scanTable thead th { white-space: nowrap; }

/* Platform column — 4 fixed slots so pills line up row-to-row (and with the header) regardless
   of which are present. Order: Android | iOS | Danger | Suspicious. Slots hold width when empty. */
#scanTable .platform-cell,
#scanTable .platform-header { white-space: nowrap; }
#scanTable .platform-inner {
  display: grid;
  /* All four slots equal width so pill-to-pill gaps are uniform.
     Pills also get min-width:100% to fill their slot — eliminates inconsistent
     centering padding that otherwise made gaps look 13/18/21 instead of equal. */
  grid-template-columns: 44px 44px 44px 44px;
  gap: 6px;
  align-items: center;
}
#scanTable .platform-slot { display: inline-flex; align-items: center; justify-content: center; min-width: 0; }
#scanTable .platform-slot .scanCountPill { margin-left: 0; }
#scanTable .platform-cell .mobBadge {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 2px 8px;
  border-radius: 999px; font-size: 12px; line-height: 1.5;
  border: 1px solid transparent;
  width: 100%;
  box-sizing: border-box;
}
#scanTable .platform-cell .mobBadge.android { background: var(--green-bg); color: var(--green); }
#scanTable .platform-cell .mobBadge.ios     { background: var(--blue-bg);  color: var(--blue); }


/* --- URL truncation with ellipsis --- */
#scanTable td.url-cell { white-space: nowrap; }
#scanTable td.url-cell,
#scanTable td.url-cell > a,
#scanTable td.url-cell > span {
  max-width: 60ch;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
}


/* --- URL cell: clean ellipsis without boxy background --- */
#scanTable td.url-cell { white-space: nowrap; }
#scanTable td.url-cell a,
#scanTable td.url-cell span {
  display: block;           /* fill cell width to avoid chip-like boxes */
  max-width: 70ch;          /* ~70 characters */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Kill any background/padding from .mono within the URL cell */
#scanTable td.url-cell .mono {
  background: transparent !important;
  padding: 0 !important;
  border-radius: 0 !important;
}
/* Optional: keep link color consistent with text */
#scanTable td.url-cell a { color: inherit; text-decoration: none; }
#scanTable td.url-cell a:hover { text-decoration: underline; }


/* --- URL cell clean display (no chip, no bars) --- */
#scanTable td.url-cell { white-space: nowrap; }
#scanTable td.url-cell .url-clip {
  display: inline-block;
  max-width: 70ch;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
  white-space: nowrap;
}
#scanTable td.url-cell .mono,
#scanTable td.url-cell a.urlLink {
  background: transparent !important;
  padding: 0 !important;
  border-radius: 0 !important;
}
#scanTable td.url-cell a.urlLink { color: inherit; text-decoration: none; }
#scanTable td.url-cell a.urlLink:hover { text-decoration: underline; }


/* --- URL cell: ellipsis on the link itself; no horizontal scroll --- */
#scanTable { width: 100%; table-layout: auto; }
#scanTable td.url-cell { white-space: nowrap; overflow: hidden; } /* allow shrinking */
#scanTable td.url-cell a.urlLink {
  display: block;
  width: 100%;
  max-width: 70ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  background: transparent !important;
  padding: 0 !important;
  border-radius: 0 !important;
  color: inherit;
  text-decoration: none;
}
#scanTable td.url-cell a.urlLink:hover { text-decoration: underline; }
/* Neutralize chip-like styles inside URL cell */
#scanTable td.url-cell .mono { background: transparent !important; padding: 0 !important; border-radius: 0 !important; }
/* Keep other columns on one line without forcing horizontal scroll */
#scanTable td:not(.url-cell) { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }


/* URL text filter */
.scan-toolbar { display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
.url-filter{ position:relative; display:inline-flex; align-items:center; }
.url-filter input#urlFilter{ height:36px; padding:6px 28px 6px 10px; border-radius:10px; border:1px solid var(--border); background:var(--card); color:var(--text); min-width:400px; }
.url-filter .clear-filter{ position:absolute; right:6px; top:50%; transform:translateY(-50%); width:20px; height:20px; line-height:18px; text-align:center; border:0; border-radius:50%; background:transparent; color:var(--text); opacity:.6; cursor:pointer; }
.url-filter .clear-filter:hover{ opacity:1; background:rgba(255,255,255,.08); }

/* Third :root block — alternate palette used by a few legacy
   sections. Same wiring strategy: alias to theme tokens with
   hex fallback so any var(--bg)/var(--card)/etc. read elsewhere
   resolves through the active theme. */
:root{
  --bg:     var(--color-bg, #0f1115);
  --card:   var(--color-bg-elevated, #111826);
  --panel:  var(--color-bg-elevated, #151b23);
  --text:   var(--color-text, #E6EAF2);
  --muted:  var(--color-text-muted, #9aa4b2);
  --border: var(--color-border-strong, #2a3242);
  --accent: var(--color-accent, #7aa2f7);
}
/* Base */
html,body{ background:var(--bg); color:var(--text); }
a{ color:var(--accent); }
.header-bar{ background: var(--panel) !important; color: var(--text) !important; }
.header-bar .navbar-brand, .header-bar .navbar-brand *{ color: var(--text) !important; }
.header-bar .text-secondary{ color: var(--muted) !important; }

/* Local aliases — tied to the global theme tokens (assets/theme.css)
   so the entire stylesheet themes automatically when html.theme-light
   is set. Fallback hex preserved for non-themed contexts. */
:root{
  --bg:     var(--color-bg, #0d1218);
  --card:   var(--color-bg-elevated, #121a23);
  --text:   var(--color-text, #f3f6f8);
  --muted:  var(--color-text-muted, #9fb3c8);
  --border: var(--color-border-strong, #1f2a36);
  --green:#2e8b57; --green-bg:#1f5131; --yg:#c2a60d; --yg-bg:#5a4a1e;
  --blue:#3b82f6; --blue-hover:#2563eb;
  --blue-bg:#1b3d5a; --red:#ef4444; --red-bg:#5a1b1b; --gray:#5b7087; --gray-bg:#2b3541;
}
/* Light-theme overrides for the custom severity hues — dark bgs would
   be invisible on a white page. */
html.theme-light {
  --green-bg: #d8f0e0;
  --yg-bg:    #fff4d4;
  --blue-bg:  #dceaf9;
  --red-bg:   #fde6e9;
  --gray-bg:  #e4e8ee;
  --green:    #1e7a3d;
  --yg:       #8a6b00;
  --blue:     #1e63cc;
  --red:      #c92a47;
  --gray:     #4b5568;
}
html,body{background:var(--bg);color:var(--text);}
/* Fade transitions */
html, body { height: 100%; }
/* Body fade — disabled during light theme to avoid the dark-flash
   that happens between page nav and the theme class being applied.
   Dark theme keeps the fade because the unstyled body bg matches. */
body{opacity:0; transition: opacity .25s ease-in-out;}
body.fade-in{opacity:1;} body.fade-out{opacity:0;}
/* Light theme: skip the fade entirely. No more dark flash. */
html.theme-light body { opacity: 1 !important; transition: none !important; }
a{color:var(--text);text-decoration:none;} a:hover{text-decoration:underline;}
.header-bar{background:var(--color-bg-elevated, #151b23);}
.card-dark{background:var(--card);border:1px solid var(--border);} 
.hero{ background: linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0.02)); border:1px solid #1e2835; border-radius:14px; }
.hero .banner{ color:#cbd5e1; font-weight:700; text-align:center; opacity:.9; }
.hero .form-wrap{ display:flex; gap:12px; justify-content:center; }
.hero .form-control{ height:48px; max-width:720px; background:#ffffff; color:#111827; border:1px solid #d1d5db; border-radius:12px; padding:10px 14px; }
.hero .form-control::placeholder{ color:#9ca3af; }
.hero .btn-scan{ height:48px; padding:0 28px; border-radius:12px; background:var(--blue); border:1px solid rgba(255,255,255,.08); color:white; font-weight:600; }
.hero .btn-scan:hover{ background:var(--blue-hover); }
.hero .inner{ padding:22px; }
@media (max-width: 768px){
  .hero .form-wrap{ flex-direction:column; }
  .hero .form-control, .hero .btn-scan{ width:100%; }
}
table.table-dark{--bs-table-bg:var(--card);--bs-table-striped-bg:#0f1620;--bs-table-hover-bg:#1a2532;color:var(--text);}
.tag{display:inline-flex;align-items:center;padding:.2rem .6rem;border-radius:999px;font-weight:700;font-size:.85rem;width:fit-content;max-width:100%;}
.status-cell { white-space:nowrap; }
/* Wrapper around the status-cell pills (primary + optional "↻ scanning" /
   "⚠ last failed" secondary). Keeps both pills as a single child of the
   mobile <td> grid layout so they share one row instead of auto-flowing
   into two grid rows. flex-wrap lets them drop to a second LINE inside the
   value cell on very narrow viewports rather than overflowing. */
.status-pills { display:inline-flex; gap:6px; align-items:center; flex-wrap:wrap; }
.tag-green{background:var(--green-bg);border:1px solid var(--green);} .tag-yellow{background:var(--yg-bg);border:1px solid var(--yg);}
.tag-blue{background:var(--blue-bg);border:1px solid #0ea5e9;} .tag-red{background:var(--red-bg);border:1px solid var(--red);}
.tabs{display:flex;gap:8px;overflow-x:auto;padding-bottom:6px;} .tabs::-webkit-scrollbar{display:none;}
.tab{all:unset;border:1px solid #2c3643;border-radius:999px;padding:8px 14px;cursor:pointer;white-space:nowrap;}
.tab.active.gray{background:var(--gray-bg);border-color:var(--gray);color:#fff;}
.tab.active.blue{background:#103d63;border-color:#0ea5e9;color:#fff;}
.tab.active.yellow{background:#403513;border-color:#c2a60d;color:#fff;}
.tab.active.green{background:#103822;border-color:#2e8b57;color:#fff;}
.tab.active.red{background:#431717;border-color:#ef4444;color:#fff;}
@media (max-width:768px){
  #scanTable thead{display:none;}
  #scanTable tbody tr{display:block;margin:12px 0;border:1px solid var(--border);border-radius:12px;background:var(--card);}
  #scanTable tbody td{display:grid;grid-template-columns:92px 1fr;gap:10px;padding:10px 12px;border:none!important;min-width:0;}
  #scanTable tbody td::before{content:attr(data-label);font-weight:700;color:#93a1b3;}
  .text-end{text-align:left!important;}
  .url-cell a, .url-cell span { display:inline; white-space:normal; word-break:break-word; overflow-wrap:anywhere; }
  .status-cell { white-space:normal; }
}

/* Rounded scan list and nowrap date column */
.table-rounded { border-radius: 14px; overflow: hidden; }
.time-cell .text-nowrap { white-space: nowrap !important; }


/* URL can wrap, other columns stay in one line */
#scanTable td.url-cell { white-space: normal !important; word-break: break-word; }
#scanTable td:not(.url-cell) { white-space: nowrap; }
/* Keep header labels on one line — .table-responsive scrolls horizontally when the viewport is tight */
#scanTable thead th { white-space: nowrap; }

/* Platform column — 4 fixed slots so pills line up row-to-row (and with the header) regardless
   of which are present. Order: Android | iOS | Danger | Suspicious. Slots hold width when empty. */
#scanTable .platform-cell,
#scanTable .platform-header { white-space: nowrap; }
#scanTable .platform-inner {
  display: grid;
  /* All four slots equal width so pill-to-pill gaps are uniform.
     Pills also get min-width:100% to fill their slot — eliminates inconsistent
     centering padding that otherwise made gaps look 13/18/21 instead of equal. */
  grid-template-columns: 44px 44px 44px 44px;
  gap: 6px;
  align-items: center;
}
#scanTable .platform-slot { display: inline-flex; align-items: center; justify-content: center; min-width: 0; }
#scanTable .platform-slot .scanCountPill { margin-left: 0; }
#scanTable .platform-cell .mobBadge {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 2px 8px;
  border-radius: 999px; font-size: 12px; line-height: 1.5;
  border: 1px solid transparent;
  width: 100%;
  box-sizing: border-box;
}
#scanTable .platform-cell .mobBadge.android { background: var(--green-bg); color: var(--green); }
#scanTable .platform-cell .mobBadge.ios     { background: var(--blue-bg);  color: var(--blue); }


/* --- URL truncation with ellipsis --- */
#scanTable td.url-cell { white-space: nowrap; }
#scanTable td.url-cell,
#scanTable td.url-cell > a,
#scanTable td.url-cell > span {
  max-width: 60ch;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
}


/* --- URL cell: clean ellipsis without boxy background --- */
#scanTable td.url-cell { white-space: nowrap; }
#scanTable td.url-cell a,
#scanTable td.url-cell span {
  display: block;           /* fill cell width to avoid chip-like boxes */
  max-width: 70ch;          /* ~70 characters */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Kill any background/padding from .mono within the URL cell */
#scanTable td.url-cell .mono {
  background: transparent !important;
  padding: 0 !important;
  border-radius: 0 !important;
}
/* Optional: keep link color consistent with text */
#scanTable td.url-cell a { color: inherit; text-decoration: none; }
#scanTable td.url-cell a:hover { text-decoration: underline; }


/* --- URL cell clean display (no chip, no bars) --- */
#scanTable td.url-cell { white-space: nowrap; }
#scanTable td.url-cell .url-clip {
  display: inline-block;
  max-width: 70ch;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: middle;
  white-space: nowrap;
}
#scanTable td.url-cell .mono,
#scanTable td.url-cell a.urlLink {
  background: transparent !important;
  padding: 0 !important;
  border-radius: 0 !important;
}
#scanTable td.url-cell a.urlLink { color: inherit; text-decoration: none; }
#scanTable td.url-cell a.urlLink:hover { text-decoration: underline; }


/* --- URL cell: ellipsis on the link itself; no horizontal scroll --- */
#scanTable { width: 100%; table-layout: auto; }
#scanTable td.url-cell { white-space: nowrap; overflow: hidden; } /* allow shrinking */
#scanTable td.url-cell a.urlLink {
  display: block;
  width: 100%;
  max-width: 70ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  background: transparent !important;
  padding: 0 !important;
  border-radius: 0 !important;
  color: inherit;
  text-decoration: none;
}
#scanTable td.url-cell a.urlLink:hover { text-decoration: underline; }
/* Neutralize chip-like styles inside URL cell */
#scanTable td.url-cell .mono { background: transparent !important; padding: 0 !important; border-radius: 0 !important; }
/* Keep other columns on one line without forcing horizontal scroll */
#scanTable td:not(.url-cell) { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }


/* ── Table column caps & scrollbar ─────────────────────────── */
/* Capped columns: truncate with ellipsis so the table never overflows */
#scanTable td.task-cell,
#scanTable td.msg-cell {
  max-width: 18ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#scanTable td[data-label="User"] {
  max-width: 16ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Styled horizontal scrollbar on the table wrapper */
.table-responsive::-webkit-scrollbar { height: 5px; }
.table-responsive::-webkit-scrollbar-track { background: transparent; }
.table-responsive::-webkit-scrollbar-thumb { background: rgba(0,229,212,.35); border-radius: 999px; }
.table-responsive::-webkit-scrollbar-thumb:hover { background: rgba(0,229,212,.65); }
.table-responsive { scrollbar-width: thin; scrollbar-color: rgba(0,229,212,.35) transparent; }

/* ── Phantom theme ───────────────────────────────────────────
   Fourth :root block in app.css — was the culprit overriding
   --panel/--bg/--card with hardcoded dark literals (came LAST in
   source order, so it won every cascade). Rewired to alias the
   global theme tokens. Light-theme overrides for the page-specific
   hues live in the html.theme-light block below this. */
:root {
  --bg:         var(--color-bg, #0d0d12);
  --card:       var(--color-bg-elevated, #16161e);
  --panel:      var(--color-bg-elevated, #1e1e2a);
  --text:       var(--color-text, #e8e8f0);
  --muted:      var(--color-text-dim, #8888aa);
  --border:     var(--color-border, rgba(0, 229, 212, 0.14));
  --accent:     var(--color-accent, #00e5d4);

  --green:      #00e5a0;
  --green-bg:   rgba(0, 229, 160, 0.11);
  --yg:         #ffb700;
  --yg-bg:      rgba(255, 183, 0, 0.11);
  --blue:       var(--color-accent, #00e5d4);
  --blue-hover: #00c4b8;
  --blue-bg:    rgba(0, 229, 212, 0.11);
  --red:        #ff4d6d;
  --red-bg:     rgba(255, 77, 109, 0.13);
  --gray:       #8888aa;
  --gray-bg:    rgba(136, 136, 170, 0.15);
}
/* Light-theme overrides for the dashboard's custom hues. */
html.theme-light {
  --green-bg:   rgba(0, 168, 100, 0.10);
  --yg-bg:      rgba(168, 120, 0, 0.10);
  --blue-bg:    rgba(0, 168, 154, 0.08);
  --red-bg:     rgba(201, 42, 71, 0.10);
  --gray-bg:    rgba(20, 30, 55, 0.06);
  --green:      #1e7a3d;
  --yg:         #a87800;
  --red:        #c92a47;
  --gray:       #4b5568;
}

/* Base */
html,body{ background:var(--bg); color:var(--text); font-family:'Inter','Segoe UI',sans-serif; }
a{ color:var(--accent); }
.header-bar{ background: var(--panel) !important; color: var(--text) !important; }
.header-bar .navbar-brand, .header-bar .navbar-brand *{ color: var(--text) !important; }
.header-bar .text-secondary{ color: var(--muted) !important; }
.header-bar i.fa-unlock-alt { color: var(--accent); }

/* Hero input — dark to match palette */
.hero .form-control {
  background: var(--card) !important;
  color:      var(--text) !important;
  border:     1px solid var(--border) !important;
}
.hero .form-control::placeholder { color: var(--muted) !important; }

/* Scan button */
.hero .btn-scan {
  background:   var(--accent) !important;
  border-color: rgba(0, 229, 212, 0.25) !important;
  color:        #0d0d12 !important;
  font-family:  'Fira Code', monospace;
  font-weight:  600;
}
.hero .btn-scan:hover { background: var(--blue-hover) !important; }

/* Pill/tag text — themed so it reads on both light + dark.
   Each .tag-X variant below carries its own color (semantic
   severity hue) which already contrasts with its tinted bg.
   Default fallback: --color-text. */
.tag { color: var(--color-text); }
.tag-green   { color: var(--green); }
.tag-yellow  { color: var(--yg); }
.tag-blue    { color: var(--blue); }
.tag-red     { color: var(--red); }
.tag-gray    { color: var(--gray); }

/* Outline-light buttons — use theme tokens so border + hover read
   on both themes (was hardcoded teal-on-rgba which vanished on white). */
.btn-outline-light { color: var(--color-text) !important; border-color: var(--color-border-strong) !important; }
.btn-outline-light:hover { background: var(--color-accent-fade) !important; color: var(--color-accent) !important; }

/* Tab active — phantom accent */
.tab.active.blue { background: rgba(0,229,212,.1); border-color: var(--accent); color: var(--accent); }

/* URL filter */
.url-filter input#urlFilter { background: var(--card); border-color: var(--border); color: var(--text); }

/* Modal */
.modal-content.bg-dark { background: var(--card) !important; color: var(--text) !important; }
.modal-content .form-control { background: var(--panel); color: var(--text); border-color: var(--border); }
.modal-content .form-control:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(0,229,212,.15); }
.modal-content .btn-primary { background: var(--accent); border-color: var(--accent); color: #0d0d12; font-weight: 600; }
.modal-content .btn-primary:hover { background: var(--blue-hover); border-color: var(--blue-hover); }

/* Table */
table.table-dark { --bs-table-bg: var(--card); --bs-table-hover-bg: rgba(0,229,212,.05); color: var(--text); }
/* ───────────────────────────────────────────────────────────── */

/* Mobile card layout: let capped cells wrap to full width instead of truncating */
@media (max-width: 768px){
  #scanTable td:not(.url-cell),
  #scanTable td.task-cell,
  #scanTable td.msg-cell,
  #scanTable td[data-label="User"] {
    max-width: none;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    word-break: break-word;
    overflow-wrap: anywhere;
  }
}

/* Mobile platform cell: each row is its own card here, so row-to-row column
   alignment isn't useful — empty Android/iOS slots just leave dead space.
   Switch from the desktop fixed 4-col grid to a left-packed flex layout and
   collapse empty slots so the pills hug the left of the value column.
   The 5th slot (slot-new) appears only on mobile — its desktop copy lives
   in the module cell, which we hide on mobile to avoid module-label wrap. */
@media (max-width: 768px) {
  #scanTable .platform-inner {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
  }
  #scanTable .platform-slot:empty {
    display: none;
  }
  #scanTable td.module-cell .scanCountNew {
    display: none;
  }
}

/* Desktop: keep the platform column 4-wide. The slot-new span is rendered in
   the DOM (so the same JS works for both viewports) but hidden here so it
   doesn't add an implicit 5th grid column. */
@media (min-width: 769px) {
  #scanTable .platform-slot.slot-new {
    display: none;
  }
}

/* Mobile URL cell layout fix.
   By default the mobile card view uses `display:grid; grid-template-columns:92px 1fr`
   on every <td>, which auto-places multiple children (icon + URL anchor) into
   SEPARATE grid rows — the icon ends up on its own line. Switch the URL cell
   to flex on mobile so the label, icon, and URL anchor share a single row.
   The URL is ellipsis-truncated; tapping it surfaces the full URL via popover. */
@media (max-width: 768px) {
  #scanTable tbody td.url-cell {
    display: flex;
    align-items: center;
    column-gap: 10px;
    flex-wrap: nowrap;
  }
  #scanTable tbody td.url-cell::before {
    flex: 0 0 92px;
    min-width: 92px;
  }
  /* URL anchor/span absorbs the remaining row width and ellipsis-truncates.
     `order: 1` pushes it to the END of the flex row so the icon (DOM order:
     comes after the URL) appears BEFORE the URL on mobile — visual order
     becomes: [::before label] [icon] [URL]. `display: block !important`
     overrides the global `inline-block !important` so flex shrink can clamp
     the width below intrinsic content. */
  #scanTable tbody td.url-cell > a,
  #scanTable tbody td.url-cell > span {
    order: 1;
    flex: 1 1 0;
    min-width: 0;
    max-width: none;
    display: block !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Icon shouldn't shrink. column-gap on the cell handles spacing on either
     side, so zero out the desktop margin-left on mobile. */
  #scanTable tbody td.url-cell > .scanExtrasIcon {
    flex: 0 0 auto;
    margin-left: 0;
  }
}

/* Danger / Suspicious count pills in the URL cell of the dashboard table */
/* Findings pills — rendered in the Actions cell (admin) or Owner cell (non-admin), right of the Re-Scan button */
.scanCountPill {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 10px; font-weight: 700; letter-spacing: .04em;
  padding: 1px 7px; border-radius: 999px;
  border: 1px solid transparent;
  vertical-align: middle;
  white-space: nowrap;
  margin-left: 6px;
}
/* Actions cell keeps Re-Scan left + pills right on a single line */
#scanTable td.actions-cell {
  white-space: nowrap;
  vertical-align: middle;
}
/* On mobile the card view gives every td a 2-col grid (label | value). The actions cell
   holds button + pills, so force flex and let the label sit before them on one row. */
@media (max-width: 768px) {
  #scanTable tbody td.actions-cell {
    display: flex !important;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
  }
  #scanTable tbody td.actions-cell::before {
    flex: 0 0 92px;
    min-width: 92px;
  }
}
.scanCountDanger { background: #ff4d6d; color: #fff; }
.scanCountWarn   { background: #ffb700; color: #0d0d12; }
.scanCountNew    { background: rgba(0,229,212,0.18); color: #00e5d4; border: 1px solid rgba(0,229,212,0.45); cursor: pointer; transition: background .12s, transform .12s; }
.scanCountNew:hover { background: rgba(0,229,212,0.32); transform: translateY(-1px); }

/* Platform header — filter pills (Android/iOS) + sort pills (danger/suspicious). */
.platform-header { white-space: nowrap; }
.platform-header .sortPill,
.platform-header .filterPill { margin-right: 0; }

/* Filter pills toggle listScans.platforms; AND-ed on the backend when multiple are active. */
.filterPill {
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px;
  padding: 2px 8px; border-radius: 999px;
  cursor: pointer; user-select: none;
  background: transparent;
  border: 1px solid;
  transition: background .12s ease, color .12s ease;
  line-height: 1.5;
  width: 100%;
  box-sizing: border-box;
}
.filterPillAndroid { color: var(--green); border-color: rgba(16,185,129,0.55); }
.filterPillIos     { color: var(--blue);  border-color: rgba(0,229,212,0.55); }
.filterPillAndroid:hover { background: var(--green-bg); }
.filterPillIos:hover     { background: var(--blue-bg); }
.filterPill.filterActive.filterPillAndroid { background: var(--green); color: #0d0d12; border-color: transparent; }
.filterPill.filterActive.filterPillIos     { background: var(--blue);  color: #0d0d12; border-color: transparent; }

/* Active-filter chips — shown next to the count info so the user always knows what's
   narrowing their results. Each chip has an × button that clears just that one filter. */
.activeFilterChip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 4px 2px 10px;
  border-radius: 999px;
  font-size: 11px; font-weight: 600;
  background: rgba(255,183,0,0.10);
  border: 1px solid rgba(255,183,0,0.35);
  color: #ffb700;
  white-space: nowrap;
  line-height: 1.6;
}
.activeFilterChip .activeFilterChipX {
  appearance: none; border: 0; background: transparent;
  color: inherit; opacity: 0.75;
  font-size: 14px; line-height: 1;
  padding: 2px 6px; margin-left: 2px;
  border-radius: 999px;
  cursor: pointer;
}
.activeFilterChip .activeFilterChipX:hover { opacity: 1; background: rgba(255,183,0,0.18); }

/* Clear-all chip — red tint to set it apart from the amber per-filter chips.
   The whole chip is the action (no inner × — chip itself is the button). */
.activeFilterChipClearAll {
  appearance: none;
  cursor: pointer;
  padding: 2px 12px;
  background: rgba(255,77,109,0.12);
  border: 1px solid rgba(255,77,109,0.45);
  color: #ff4d6d;
  font: inherit;
  font-size: 11px; font-weight: 600;
  border-radius: 999px;
  display: inline-flex; align-items: center; gap: 4px;
  white-space: nowrap;
  line-height: 1.6;
  transition: background .12s ease;
}
.activeFilterChipClearAll:hover { background: rgba(255,77,109,0.22); }
.sortPill {
  display: inline-flex; align-items: center; justify-content: center; gap: 3px;
  font-size: 10px; font-weight: 700; letter-spacing: .04em;
  padding: 1px 7px; border-radius: 999px;
  cursor: pointer; user-select: none;
  background: transparent;
  border: 1px solid;
  transition: background .12s ease, opacity .12s ease;
  line-height: 1.5;
  width: 100%;
  box-sizing: border-box;
}
.sortPillDanger { color: #ff4d6d; border-color: rgba(255,77,109,0.55); }
.sortPillWarn   { color: #ffb700; border-color: rgba(255,183,0,0.55); }
.sortPillDanger:hover { background: rgba(255,77,109,0.14); }
.sortPillWarn:hover   { background: rgba(255,183,0,0.14); }
.sortPill.sortActive.sortPillDanger { background: #ff4d6d; color: #fff; border-color: transparent; }
.sortPill.sortActive.sortPillWarn   { background: #ffb700; color: #0d0d12; border-color: transparent; }
.sortArrow { font-size: 11px; line-height: 1; }

/* Findings filter — native select with a custom chevron, matches the dark theme */
.findings-filter-wrap {
  position: relative;
  display: inline-flex;
  flex: 0 0 auto;
}
.findings-filter {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  background: var(--card, #121a23);
  color: var(--text, #f3f6f8);
  border: 1px solid var(--border, #1f2a36);
  border-radius: 10px;
  padding: 7px 34px 7px 12px;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  min-width: 180px;
}
.findings-filter:hover  { border-color: #00e5d4; }
.findings-filter:focus  { outline: none; border-color: #00e5d4; box-shadow: 0 0 0 3px rgba(0,229,212,0.25); }
.findings-filter option { background: var(--card, #121a23); color: var(--text, #f3f6f8); }
.findings-filter-chev {
  position: absolute;
  right: 14px; top: 50%;
  width: 7px; height: 7px;
  pointer-events: none;
  border-right: 2px solid var(--muted, #9fb3c8);
  border-bottom: 2px solid var(--muted, #9fb3c8);
  transform: translateY(-70%) rotate(45deg);
}

/* Scan form — Advanced section (custom header + proxy country).
   Width matches the input (720) + 12px gap + Scan button (~96) so the panel
   aligns with the row above instead of looking inset. */
.scan-advanced-wrap { max-width: 832px; margin-left: auto; margin-right: auto; }
#scanPrefillNote { max-width: 832px !important; }
.scan-advanced-wrap #scanAdvancedToggle { text-decoration: none; }
.scan-advanced-wrap #scanAdvancedToggle:hover { color: var(--accent) !important; }
.scan-advanced-wrap #scanAdvancedToggle[aria-expanded="true"] #scanAdvancedCaret { transform: rotate(90deg); }
.scan-advanced-wrap #scanAdvancedCaret { transition: transform .15s ease; display: inline-block; }
.scan-advanced { background: var(--card, #14141c); border: 1px solid var(--border, #262633); border-radius: 10px; padding: 12px 14px; }

/* Force all three controls in the advanced panel to look identical: same
   height, padding, border-radius, background. Inputs would otherwise pick up
   the bigger .hero .form-control rule (48px / radius 12px) and the select
   would stay at Bootstrap's form-select-sm default (~26px / radius 4px). */
.scan-advanced .form-control,
.scan-advanced .form-select {
  height: 36px !important;
  padding: 6px 10px !important;
  font-size: 13px !important;
  line-height: 1.3 !important;
  border-radius: 8px !important;
  background: var(--panel, #0f0f17) !important;
  color: var(--text, #e8e8f0) !important;
  border: 1px solid var(--border, #262633) !important;
}
.scan-advanced .form-control:focus,
.scan-advanced .form-select:focus {
  border-color: var(--accent, #00e5d4) !important;
  box-shadow: 0 0 0 3px rgba(0,229,212,.15) !important;
}
.scan-advanced .form-control::placeholder { color: var(--muted, #8888aa); }
.scan-advanced .form-label { color: var(--muted, #8888aa); margin-bottom: 4px; }

/* Re-add a dropdown caret on the select. With appearance:none and no
   background-image set anywhere, the arrow disappears entirely; Bootstrap's
   default caret SVG is dark and gets eaten by the dark panel. Use a teal
   chevron tinted with the accent so it's visible on the dark background.
   (Only applies to plain <select>s — the custom searchDd uses an icon.) */
.scan-advanced .form-select:not(.searchDd-native) {
  padding-right: 28px !important;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%2300e5d4' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e") !important;
  background-repeat: no-repeat !important;
  background-position: right 10px center !important;
  background-size: 12px 10px !important;
}

/* Searchable dropdown — custom widget that wraps a native <select>. The
   native element stays in the DOM (hidden) so form submission and external
   .value reads keep working; the visible UI is a trigger button + floating
   panel with a search input and filterable list. */
.searchDd { position: relative; display: block; width: 100%; }
.searchDd-native { display: none !important; }
.searchDd-trigger {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; text-align: left; cursor: pointer;
}
.searchDd-trigger .searchDd-label {
  flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: var(--text, #e8e8f0);
}
.searchDd-trigger .searchDd-label:empty::before {
  content: '— None —'; color: var(--muted, #8888aa);
}
.searchDd-caret { color: var(--accent, #00e5d4); font-size: 11px; margin-left: 8px; transition: transform .15s ease; }
.searchDd-trigger[aria-expanded="true"] .searchDd-caret { transform: rotate(180deg); }
.searchDd-panel {
  position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 1050;
  background: var(--panel, #0f0f17);
  border: 1px solid var(--border, #262633);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .5);
  padding: 6px;
  max-height: 320px; display: flex; flex-direction: column;
}
.searchDd-search {
  height: 32px !important; padding: 4px 10px !important; font-size: 13px !important;
  background: var(--card, #14141c) !important;
  color: var(--text, #e8e8f0) !important;
  border: 1px solid var(--border, #262633) !important;
  border-radius: 6px !important;
  margin-bottom: 6px !important;
}
.searchDd-search:focus {
  border-color: var(--accent, #00e5d4) !important;
  box-shadow: 0 0 0 2px rgba(0, 229, 212, .15) !important;
}
.searchDd-list {
  flex: 1; overflow-y: auto;
  scrollbar-width: thin; scrollbar-color: var(--accent, #00e5d4) transparent;
}
.searchDd-list::-webkit-scrollbar { width: 8px; }
.searchDd-list::-webkit-scrollbar-track { background: transparent; }
.searchDd-list::-webkit-scrollbar-thumb { background: rgba(0, 229, 212, .25); border-radius: 4px; }
.searchDd-list::-webkit-scrollbar-thumb:hover { background: rgba(0, 229, 212, .45); }
.searchDd-item {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: transparent; border: 0; padding: 6px 10px;
  font-size: 13px; color: var(--text, #e8e8f0);
  border-radius: 4px;
}
.searchDd-item:hover,
.searchDd-item.is-active {
  background: rgba(0, 229, 212, .12);
  color: var(--accent, #00e5d4);
}
#scanPrefillNote { max-width: 720px; }
#scanPrefillClearBtn { text-decoration: underline; }

/* Dashboard row chips — kept for use on the report header (view_report.css
   mirrors these styles). On the dashboard URL cell the chips are replaced
   by a single .scanExtrasIcon button with a custom popover. */
.scanExtraChip { display: inline-flex; align-items: center; gap: 4px; padding: 1px 6px; margin-left: 6px; border-radius: 999px; font-size: .65rem; font-weight: 600; line-height: 1.4; vertical-align: middle; border: 1px solid transparent; }
.scanExtraChip.hdr { background: rgba(0, 229, 212, .12); color: var(--accent, #00e5d4); border-color: rgba(0, 229, 212, .35); }
.scanExtraChip.cty { background: rgba(120, 180, 255, .14); color: #9bbcff; border-color: rgba(120, 180, 255, .35); }

/* Keep the URL cell on a single line when an info icon precedes the URL link.
   The earlier .url-cell rules give the <a>/<span> display:block, which would
   push a sibling button onto a second row. Switching the inner element to
   inline-block keeps the cell as a normal table-cell (so column widths line up
   across rows) while still allowing text-overflow ellipsis. */
#scanTable td.url-cell > a,
#scanTable td.url-cell > span { display: inline-block !important; vertical-align: middle; max-width: calc(100% - 26px); }
#scanTable td.url-cell > .scanExtrasIcon { vertical-align: middle; }

/* URL-cell info icon — single indicator that a scan was started with custom
   header or proxy country. On desktop it's rendered after the URL (right
   side); on mobile a flex `order` swap places it before the URL (left side,
   between the cell label and the URL text) so the URL never wraps the icon
   onto its own line. Hover or click reveals the .scanExtrasPop popover. */
.scanExtrasIcon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  margin-left: 8px; padding: 0; vertical-align: middle;
  background: transparent; border: 0; cursor: pointer;
  color: var(--accent, #00e5d4); font-size: 14px; line-height: 1;
  border-radius: 50%;
  transition: color .12s ease, background .12s ease, transform .12s ease;
}
.scanExtrasIcon:hover, .scanExtrasIcon:focus-visible {
  color: #fff; background: rgba(0, 229, 212, .25); outline: none;
  transform: scale(1.08);
}

/* Floating popover that surfaces the custom header / proxy country values
   when the user hovers or clicks the URL-cell info icon. Singleton — one
   element reused for every row, positioned by JS. */
.scanExtrasPop {
  position: absolute; z-index: 1080;
  min-width: 240px; max-width: 380px;
  background: var(--panel, #0f0f17);
  border: 1px solid rgba(0, 229, 212, .35);
  border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, .55);
  padding: 10px 12px;
  color: var(--text, #e8e8f0);
  font-size: 12px; line-height: 1.4;
}
.scanExtrasPop[hidden] { display: none; }
/* Arrow above the popover, horizontally aligned to the icon via --arrow-x. */
.scanExtrasPop::before {
  content: ''; position: absolute;
  top: -7px; left: var(--arrow-x, 50%); transform: translateX(-50%);
  width: 12px; height: 12px;
  background: var(--panel, #0f0f17);
  border-left: 1px solid rgba(0, 229, 212, .35);
  border-top:  1px solid rgba(0, 229, 212, .35);
  rotate: 45deg;
}
.scanExtrasPop-row { padding: 4px 0; }
.scanExtrasPop-row + .scanExtrasPop-row { border-top: 1px solid rgba(255, 255, 255, .06); margin-top: 4px; padding-top: 8px; }
.scanExtrasPop-label { display: flex; align-items: center; gap: 6px; color: var(--muted, #8888aa); font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; margin-bottom: 4px; }
.scanExtrasPop-label i { color: var(--accent, #00e5d4); }
.scanExtrasPop-value { font-family: 'Fira Code', monospace; font-size: 12px; word-break: break-all; }
.scanExtrasPop-key { color: var(--accent, #00e5d4); font-weight: 600; }
.scanExtrasPop-sep { color: var(--muted, #8888aa); margin: 0 4px; }
.scanExtrasPop-val { color: var(--text, #e8e8f0); }

/* NOTE: The Module dependency map styles live in view_report.css, not
   here. The report viewer loads view_report.css; app.css drives the
   dashboard. Putting dep-map rules in this file silently no-ops on
   the page that actually needs them. */

/* Dashboard view-mode toggle. Two-button segmented control replacing the
   old single button that flipped its label between action ("Group by app")
   and status ("Showing 1 row per app"). Both options always visible; the
   active mode is highlighted with the teal accent so the current state
   reads at a glance without the user having to reason about which label
   means what. JS handler in assets/app.js mirrors App.state.dedup onto
   .active here. */
.dashViewToggle {
  display: inline-flex;
  border: 1px solid var(--color-border, #2c3643);
  border-radius: 999px;
  overflow: hidden;
  background: var(--color-bg-overlay);
}
.dashViewBtn {
  border: 0;
  background: transparent;
  color: var(--color-text-muted, #adb5bd);
  font-size: 0.78rem;
  font-weight: 500;
  padding: 0.35rem 0.85rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  white-space: nowrap;
  transition: color 120ms ease, background 120ms ease;
}
.dashViewBtn:hover:not(.active) { color: var(--color-text); background: var(--color-bg-overlay); }
.dashViewBtn.active {
  background: var(--color-accent-fade);
  color: var(--color-accent);
  font-weight: 600;
}
.dashViewBtn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

/* ─────────────────────────────────────────────────────────────────────
 * Advanced Filters Modal — card-per-filter layout. Each filter type
 * (server action / data fetch / resource) gets its own card with a
 * typed FA icon, title, and hint, instead of three identical-looking
 * label/input/help-text triplets in plain Bootstrap. Scoped to
 * .advFiltersModal so the New Findings modal next door isn't affected.
 *
 * Source-of-truth markup: dashboard.php #adminFiltersModal.
 * JS handler: assets/app.js (the IIFE that wires applyAdvancedFilters).
 * ───────────────────────────────────────────────────────────────────── */
.advFiltersModal .modal-content {
  background: var(--card, #16161e);
  color: var(--text, #e8e8f0);
  border: 1px solid rgba(0,229,212,0.18);
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0,0,0,0.5);
}
.advFiltersModal .modal-header {
  border-bottom: 1px solid rgba(255,255,255,0.06);
  padding: 16px 20px;
  align-items: flex-start;
}
.advFiltersModal .modal-title {
  font-size: 1.05rem; font-weight: 600;
  display: inline-flex; align-items: center; gap: 8px;
  margin: 0;
}
.advFiltersTitleIcon { color: #00e5d4; }
.advFiltersSubtitle {
  color: var(--muted, #8888aa); font-size: 12px;
  margin-top: 4px; max-width: 560px;
  line-height: 1.45;
}
.advFiltersModal .modal-body {
  padding: 16px 20px;
  display: flex; flex-direction: column; gap: 12px;
}
.advFilterCard {
  background: rgba(0,0,0,0.18);
  border: 1px solid rgba(255,255,255,0.06);
  border-radius: 10px;
  padding: 12px 14px;
  display: flex; flex-direction: column; gap: 10px;
  /* Focus-within lights up the border so the user gets feedback about
     which card is active — much more legible than the default Bootstrap
     input-only focus ring at this card density. */
  transition: border-color 120ms ease, background 120ms ease;
}
.advFilterCard:focus-within {
  border-color: rgba(0,229,212,0.45);
  background: rgba(0,0,0,0.28);
}
.advFilterCardHead {
  display: flex; align-items: flex-start; gap: 12px;
}
.advFilterIcon {
  width: 36px; height: 36px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 8px;
  background: rgba(0,229,212,0.10);
  color: #00e5d4;
  font-size: 14px;
  flex: 0 0 auto;
}
.advFilterCardLabel {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0;
}
.advFilterCardTitle {
  font-size: 0.92rem; font-weight: 600;
  color: var(--text, #e8e8f0);
}
.advFilterCardHint {
  font-size: 0.75rem; color: var(--muted, #8888aa);
  line-height: 1.4;
}
.advFilterCardHint code {
  background: rgba(0,229,212,0.08);
  color: #00e5d4;
  padding: 0 4px;
  border-radius: 4px;
  font-size: 0.95em;
}
.advFilterInput {
  width: 100%;
  background: rgba(0,0,0,0.35);
  border: 1px solid rgba(255,255,255,0.10);
  color: var(--text, #e8e8f0);
  border-radius: 8px;
  padding: 8px 12px;
  font-size: 0.9rem;
  font-family: 'Inter', system-ui, sans-serif;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.advFilterInput::placeholder { color: rgba(232,232,240,0.32); }
.advFilterInput:focus {
  outline: none;
  border-color: rgba(0,229,212,0.55);
  box-shadow: 0 0 0 2px rgba(0,229,212,0.15);
}
.advFiltersModal .modal-footer {
  border-top: 1px solid rgba(255,255,255,0.06);
  padding: 12px 20px;
  display: flex; justify-content: space-between; gap: 8px;
}
.advFilterBtnClear {
  border: 0; background: transparent;
  color: var(--muted, #8888aa);
  font-size: 0.85rem; font-weight: 500;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px;
  border-radius: 8px;
  cursor: pointer;
  transition: color 120ms ease, background 120ms ease;
}
.advFilterBtnClear:hover { color: var(--text, #e8e8f0); background: rgba(255,255,255,0.05); }
.advFilterBtnApply {
  border: 0;
  background: #00e5d4;
  color: #0b1220;
  font-weight: 700;
  font-size: 0.9rem;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 16px;
  border-radius: 8px;
  cursor: pointer;
  transition: background 120ms ease, transform 80ms ease;
}
.advFilterBtnApply:hover { background: #00c4b5; }
.advFilterBtnApply:active { transform: translateY(1px); }

/* Dashboard Module-column link → /app-history/{id}. Subtle until hover
   so the row's compact density doesn't change; on hover the user gets
   the standard "this is clickable" affordance (color + underline). The
   anchor uses stopPropagation in JS so clicking it doesn't also trigger
   the row-level click handler that opens the report — Module name and
   URL are deliberately separate destinations now. */
.moduleHistoryLink {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px dotted transparent;
  transition: color 120ms, border-color 120ms;
}
.moduleHistoryLink:hover,
.moduleHistoryLink:focus-visible {
  color: var(--accent, #00e5d4);
  border-bottom-color: rgba(0,229,212,0.45);
  outline: none;
}
