/*
 * RedPotato design system.
 *
 * Layered over Bootstrap 4 rather than replacing it: the modals depend on
 * Bootstrap's JavaScript, so the component markup stays and this file restyles
 * it. Bootstrap 4 compiles its colours in (custom properties arrived in 5), so
 * the themable surface is defined here and the components in actual use are
 * repointed at it.
 *
 * Everything is driven by the tokens in :root. A theme is a different set of
 * token values, nothing more.
 */

/* ---------------------------------------------------------------- tokens */

:root {
  /* Neutral ramp. Slate rather than pure grey: long sessions in front of
     dense tables, and a slight blue cast is easier to sit with. */
  --rp-bg:            #f6f7f9;
  --rp-surface:       #ffffff;
  --rp-surface-2:     #eef0f4;
  --rp-surface-3:     #e4e7ec;
  --rp-border:        #d8dce3;
  --rp-border-strong: #b9c0cb;

  --rp-text:          #1c2230;
  --rp-text-muted:    #667085;
  --rp-text-faint:    #98a2b3;

  /* The logo is red, so red is the accent rather than Bootstrap's blue. */
  --rp-accent:        #c0392b;
  --rp-accent-hover:  #a93226;
  --rp-accent-soft:   #fdeceb;
  --rp-accent-text:   #ffffff;

  /* Status. Used for scope, ping, port state and task state alike, so they
     have to read at badge size, not just as large fills. */
  --rp-ok:            #1a7f4b;
  --rp-ok-soft:       #e3f5ea;
  --rp-warn:          #a16207;
  --rp-warn-soft:     #fdf3d7;
  --rp-bad:           #b42318;
  --rp-bad-soft:      #fde8e6;
  --rp-info:          #175cd3;
  --rp-info-soft:     #e6effd;
  --rp-neutral:       #475467;
  --rp-neutral-soft:  #eceef2;

  --rp-shadow:        0 1px 2px rgba(16, 24, 40, .06), 0 1px 3px rgba(16, 24, 40, .10);
  --rp-shadow-lg:     0 8px 24px rgba(16, 24, 40, .12);

  /* Two steps, mapped onto the same ratio OASM's own scale uses (sm 6 /
     md 9 / lg 12): the small token is what buttons, inputs and badges
     take, the large one what cards and panels take. 6/4 read as hard
     corners next to it -- this is the single most visible difference
     between the two interfaces. */
  --rp-radius:        10px;
  --rp-radius-sm:     6px;

  --rp-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* Addresses, ports, tokens and banners are identifiers. Tabular figures and
     a fixed advance make a column of IPs scannable instead of ragged. */
  --rp-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
}

:root[data-theme="dark"] {
  --rp-bg:            #0f1419;
  --rp-surface:       #171d26;
  --rp-surface-2:     #1e2631;
  --rp-surface-3:     #27313f;
  --rp-border:        #2c3745;
  --rp-border-strong: #3d4a5c;

  --rp-text:          #e4e8ef;
  --rp-text-muted:    #97a3b6;
  --rp-text-faint:    #6b7789;

  --rp-accent:        #e05a4a;
  --rp-accent-hover:  #eb6d5d;
  --rp-accent-soft:   #35211f;
  --rp-accent-text:   #ffffff;

  --rp-ok:            #4ade80;
  --rp-ok-soft:       #14301f;
  --rp-warn:          #fbbf24;
  --rp-warn-soft:     #33280c;
  --rp-bad:           #f87171;
  --rp-bad-soft:      #351a19;
  --rp-info:          #7cb0f7;
  --rp-info-soft:     #16243a;
  --rp-neutral:       #9aa5b8;
  --rp-neutral-soft:  #232c39;

  --rp-shadow:        0 1px 2px rgba(0, 0, 0, .4);
  --rp-shadow-lg:     0 12px 32px rgba(0, 0, 0, .55);
}

/* ---------------------------------------------------------------- base */

html { -webkit-text-size-adjust: 100%; }

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

h2, h3, h4, h5 {
  color: var(--rp-text);
  font-weight: 600;
  letter-spacing: -0.01em;
}
h2 { font-size: 1.5rem; }
h3 { font-size: 1.25rem; margin-bottom: .25rem; }
h4 { font-size: 1rem; margin-top: 1.75rem; margin-bottom: .75rem; }
h5 { font-size: .9rem; }

a { color: var(--rp-accent); }
a:hover { color: var(--rp-accent-hover); }

/* One keyboard-focus ring for everything, rather than per-component rules
   that only ever covered a couple of places. :focus-visible, not :focus,
   so it appears for keyboard and assistive navigation and stays out of
   the way of mouse clicks -- Bootstrap 4 shipped before the pseudo-class
   existed and rings on plain :focus instead.
   .form-control/.custom-select are excluded on purpose: they already
   carry their own accent border + soft halo (see the forms section), and
   would otherwise wear two rings at once. */
:focus-visible {
  outline: 2px solid var(--rp-accent);
  outline-offset: 2px;
}
.form-control:focus-visible, .custom-select:focus-visible { outline: 0; }

code, kbd, samp, pre {
  font-family: var(--rp-mono);
  font-size: .86em;
}
code {
  background: var(--rp-surface-2);
  color: var(--rp-text);
  padding: .1em .4em;
  border-radius: var(--rp-radius-sm);
}
pre {
  background: var(--rp-surface-2);
  color: var(--rp-text);
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-radius);
  padding: .75rem 1rem;
  max-height: 26rem;
  overflow: auto;
}

.text-muted { color: var(--rp-text-muted) !important; }

/* ---------------------------------------------------------------- layout */

.rp-main { padding-top: 1.5rem; padding-bottom: 4rem; }

/* The page title and its subtitle were two headings stacked with no relation;
   this ties them together and gives the toolbar something to sit against. */
.rp-page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .25rem .75rem;
  margin-bottom: 1.25rem;
}
.rp-page-head h3 { margin: 0; }
.rp-page-head .rp-page-sub {
  color: var(--rp-text-muted);
  font-size: .95rem;
}

/* error.php (403/404): centered rather than left-aligned like every other
   page, since there is no table or form beneath it to align against. */
.rp-error-page {
  max-width: 32rem;
  margin: 3rem auto;
  text-align: center;
}
.rp-error-page .rp-error-icon { color: var(--rp-text-faint); margin-bottom: 1rem; }
.rp-error-page .rp-error-icon .rp-nav-icon { width: 4rem; height: 4rem; }
.rp-error-page .rp-error-code {
  font-family: var(--rp-mono);
  font-size: .95rem;
  color: var(--rp-text-faint);
  letter-spacing: .05em;
}
.rp-error-page h3 { margin: .25rem 0 .75rem; }
.rp-error-page .rp-page-sub { display: block; margin-bottom: 1.5rem; }

/* Action bars: replaces eight repetitions of style="margin: 10px" */
.rp-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: center;
  margin: 0 0 1.25rem;
}

/* ---------------------------------------------------------------- sidebar */

:root {
  --rp-sidebar-w: 220px;
  --rp-sidebar-w-collapsed: 56px;
}

.rp-sidebar {
  position: fixed;
  inset: 0 auto 0 0;
  width: var(--rp-sidebar-w);
  display: flex;
  flex-direction: column;
  background: var(--rp-surface);
  border-right: 1px solid var(--rp-border);
  z-index: 1030;
  transition: width .15s ease, transform .15s ease;
}
:root[data-sidebar="collapsed"] .rp-sidebar { width: var(--rp-sidebar-w-collapsed); }

.rp-content {
  margin-left: var(--rp-sidebar-w);
  min-height: 100vh;
  transition: margin-left .15s ease;
}
:root[data-sidebar="collapsed"] .rp-content { margin-left: var(--rp-sidebar-w-collapsed); }

.rp-sidebar-header {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .7rem .6rem;
  border-bottom: 1px solid var(--rp-border);
  flex-shrink: 0;
}
.rp-sidebar-brand {
  display: flex;
  align-items: center;
  gap: .55rem;
  min-width: 0;
  color: var(--rp-text);
  text-decoration: none;
}
.rp-sidebar-brand:hover { color: var(--rp-text); text-decoration: none; }
.rp-sidebar-brand img { width: 26px; height: auto; flex-shrink: 0; display: block; }
.rp-sidebar-brand-text { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.rp-sidebar-collapse, .rp-sidebar-close {
  margin-left: auto;
  flex-shrink: 0;
  background: transparent;
  border: 0;
  color: var(--rp-text-muted);
  width: 28px;
  height: 28px;
  padding: 0;
  border-radius: var(--rp-radius-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
.rp-sidebar-collapse:hover, .rp-sidebar-close:hover { color: var(--rp-text); background: var(--rp-surface-2); }
.rp-sidebar-collapse svg { width: 16px; height: 16px; transition: transform .15s ease; }
:root[data-sidebar="collapsed"] .rp-sidebar-collapse svg { transform: rotate(180deg); }
.rp-sidebar-close { display: none; }

/* Icon-only rail: hide every label, center what is left, drop the search
   form (a text input has nowhere useful to go at 56px). The link itself
   stays the full hit target, not just the icon. */
:root[data-sidebar="collapsed"] .rp-nav-label,
:root[data-sidebar="collapsed"] .rp-nav-group-label,
:root[data-sidebar="collapsed"] .rp-nav-search,
:root[data-sidebar="collapsed"] .rp-sidebar-user { display: none; }
:root[data-sidebar="collapsed"] .rp-nav-link { justify-content: center; }
:root[data-sidebar="collapsed"] .rp-sidebar-brand { justify-content: center; }

.rp-nav-icon { width: 18px; height: 18px; flex-shrink: 0; }

.rp-sidebar-nav {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  padding: .6rem .5rem;
}
.rp-nav-group { margin-bottom: 1rem; }
.rp-nav-group-label {
  padding: .4rem .6rem .3rem;
  font-size: .68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--rp-text-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Sits under the project name in the sidebar, not beside it: the label
   itself clips with an ellipsis, which would hide this on exactly the
   projects with the longest names. */
.rp-nav-group-note { padding: 0 .6rem .35rem; }
:root[data-sidebar="collapsed"] .rp-nav-group-note { display: none; }

.rp-nav-list { list-style: none; margin: 0; padding: 0; }

.rp-nav-link {
  position: relative;
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .45rem .6rem;
  margin: .05rem 0;
  border-radius: var(--rp-radius-sm);
  color: var(--rp-text-muted);
  font-size: .85rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
}
.rp-nav-link:hover { color: var(--rp-text); background: var(--rp-surface-2); text-decoration: none; }
.rp-nav-link.active {
  color: var(--rp-accent);
  background: var(--rp-accent-soft);
  font-weight: 600;
}
/* The accent bar sits just inside the sidebar's own edge, not the link's
   padding box, so it lines up the same way regardless of how deep the link
   itself is nested. */
.rp-nav-link.active::before {
  content: "";
  position: absolute;
  left: -.5rem;
  top: 20%;
  bottom: 20%;
  width: 3px;
  border-radius: 2px;
  background: var(--rp-accent);
}
.rp-nav-label { overflow: hidden; text-overflow: ellipsis; }

.rp-nav-search {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin: .4rem .6rem 0;
  padding: .3rem .5rem;
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-radius-sm);
  background: var(--rp-surface-2);
}
.rp-nav-search .rp-nav-icon { color: var(--rp-text-faint); }
.rp-nav-search input {
  border: 0;
  background: transparent;
  padding: 0;
  height: auto;
  font-size: .8rem;
}
.rp-nav-search input:focus { box-shadow: none; }

.rp-sidebar-footer {
  flex-shrink: 0;
  padding: .6rem .5rem;
  border-top: 1px solid var(--rp-border);
}
.rp-sidebar-user {
  display: block;
  padding: .3rem .6rem;
  font-size: .78rem;
  font-weight: 600;
  color: var(--rp-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rp-mobile-toggle {
  display: none;
  position: fixed;
  top: .6rem;
  left: .6rem;
  z-index: 1025;
  background: var(--rp-surface);
  border: 1px solid var(--rp-border);
  color: var(--rp-text);
  width: 36px;
  height: 36px;
  border-radius: var(--rp-radius-sm);
  align-items: center;
  justify-content: center;
  box-shadow: var(--rp-shadow);
}
.rp-mobile-toggle svg { width: 18px; height: 18px; }

.rp-sidebar-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(16, 20, 27, .45);
  z-index: 1029;
}

/* ---------------------------------------------------------------- theme toggle */

.rp-theme-toggle {
  background: transparent;
  border: 1px solid var(--rp-border);
  color: var(--rp-text-muted);
  border-radius: var(--rp-radius-sm);
  width: 32px;
  height: 32px;
  line-height: 1;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.rp-theme-toggle:hover { color: var(--rp-text); background: var(--rp-surface-2); }
.rp-theme-toggle svg { width: 16px; height: 16px; fill: currentColor; }
/* Show the icon for the theme you would switch to */
.rp-theme-toggle .rp-icon-dark { display: none; }
:root[data-theme="dark"] .rp-theme-toggle .rp-icon-dark { display: block; }
:root[data-theme="dark"] .rp-theme-toggle .rp-icon-light { display: none; }

/* ---------------------------------------------------------------- cards */

.rp-card {
  background: var(--rp-surface);
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-radius);
  box-shadow: var(--rp-shadow);
}

/* Dashboard counters. Replaces AdminLTE's small-box, which was the only thing
   the 90 KB stylesheet was still being loaded for. */
.rp-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1rem;
  margin-bottom: 2rem;
}
.rp-stat {
  background: var(--rp-surface);
  border: 1px solid var(--rp-border);
  border-left: 3px solid var(--rp-stat-accent, var(--rp-accent));
  border-radius: var(--rp-radius);
  padding: 1rem 1.1rem;
  box-shadow: var(--rp-shadow);
}
.rp-stat .rp-stat-value {
  font-family: var(--rp-mono);
  font-size: 1.75rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  color: var(--rp-text);
}
.rp-stat .rp-stat-label {
  color: var(--rp-text-muted);
  font-size: .78rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  margin-top: .35rem;
}
.rp-stat a { font-size: .8rem; }
.rp-stat--domains { --rp-stat-accent: var(--rp-info); }
.rp-stat--subdomains { --rp-stat-accent: var(--rp-ok); }
.rp-stat--hosts { --rp-stat-accent: var(--rp-warn); }
.rp-stat--ports { --rp-stat-accent: var(--rp-bad); }

/* Dashboard summary cards: Findings/Tasks/Recent activity/Top ports/Top tech. */
.rp-dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1rem;
  margin-bottom: 2rem;
}
.rp-dashboard-grid .rp-card h5 {
  margin-bottom: .75rem;
}
.rp-dashboard-grid .rp-card table td {
  border-top: none;
  padding: .25rem 0;
}

.rp-tech-icon { vertical-align: -2px; margin-right: .3em; }

/* Dashboard trend rows: a measured count, its change over the window, and
   the shape of how it got there. Two of them stacked, so they share a
   baseline and can be compared at a glance. */
.rp-trend { margin-bottom: .85rem; }
.rp-trend:last-of-type { margin-bottom: 1rem; }

.rp-trend-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .5rem;
  margin-bottom: .1rem;
}
.rp-trend-label {
  font-size: .75rem;
  font-weight: 500;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--rp-text-muted);
}
a.rp-trend-label:hover { color: var(--rp-accent); }

.rp-trend-delta { font-size: .75rem; font-variant-numeric: tabular-nums; }
/* good/bad rather than up/down: the two rows judge in opposite directions
   (fewer findings is better, more discovered surface is better), and the
   arrow beside this already says which way the number moved. */
.rp-trend-delta--good { color: var(--rp-ok); }
.rp-trend-delta--bad  { color: var(--rp-bad); }
.rp-trend-delta--flat { color: var(--rp-text-faint); }

.rp-trend-body {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.rp-trend-value {
  font-family: var(--rp-mono);
  font-variant-numeric: tabular-nums;
  font-size: 1.6rem;
  font-weight: 600;
  line-height: 1.1;
  color: var(--rp-text);
}

/* render_sparkline()'s own two shapes. currentColor throughout, so one
   helper serves any context that sets a text colour. */
.rp-spark { display: block; color: var(--rp-text-muted); overflow: visible; }
.rp-spark-line { fill: none; stroke: currentColor; stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.rp-spark-fill { fill: currentColor; opacity: .13; stroke: none; }
.rp-trend .rp-spark { color: var(--rp-accent); }

/* Loading placeholders and height padding for a server-side table, see
   rpDataTableSkeleton() in js/redpotato.js. Both are rows DataTables does
   not know about: it rebuilds the tbody on every draw, which clears them.

   The widths cycle rather than being uniform so the block reads as rows of
   text rather than as a grid of identical bars -- a real row of an IP, a
   PTR and a badge is ragged, and a skeleton that is not looks like a
   rendering fault. */
.rp-skeleton td, .rp-rowpad td { border-top: 1px solid var(--rp-border); }
.rp-skeleton td > span {
  display: block;
  height: .7rem;
  border-radius: var(--rp-radius-sm);
  background: var(--rp-surface-3);
  animation: rp-skeleton-pulse 1.3s ease-in-out infinite;
}
.rp-skeleton td:nth-child(3n+1) > span { width: 62%; }
.rp-skeleton td:nth-child(3n+2) > span { width: 88%; }
.rp-skeleton td:nth-child(3n+3) > span { width: 45%; }
/* Staggered, so the block breathes rather than blinking as one slab. */
.rp-skeleton:nth-child(even) td > span { animation-delay: .18s; }

/* Pure spacers: the same height as a row, holding nothing. */
.rp-rowpad td > span { display: block; height: .7rem; }

@keyframes rp-skeleton-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .45; }
}
@media (prefers-reduced-motion: reduce) {
  .rp-skeleton td > span { animation: none; }
}

/* One collapsible header row per run of a grouped column -- see
   rpDataTableRowGroup() in js/redpotato.js. Tinted like the selection bar's
   own header cell, for the same reason: a table striped by ordinary rows
   needs its group boundaries to read as a different kind of row, not just
   another one of them. */
.rp-group-header { cursor: pointer; }
.rp-group-header td {
  background: var(--rp-surface-2);
  border-top: 1px solid var(--rp-border);
  border-bottom: 1px solid var(--rp-border);
  font-weight: 500;
}
.rp-group-header:hover td { background: var(--rp-accent-soft); }
.rp-group-arrow { display: inline-block; width: 1em; color: var(--rp-text-muted); }

/* The Endpoints page's directory-tree view -- see the tree-building script
   in endpoints.php. Plain nested <ul>s, indented by Bootstrap's own list
   reset undone one level at a time; no tree-view library, same reasoning
   rpDataTableRowGroup() already follows for the table's own grouping. */
.rp-tree, .rp-tree ul { list-style: none; margin: 0; padding-left: 1.25rem; }
.rp-tree { padding-left: 0; }
.rp-tree-node { padding: .15rem 0; }
.rp-tree-label { display: inline-flex; align-items: center; gap: .3rem; }
.rp-tree-dir > .rp-tree-label { cursor: pointer; }
.rp-tree-dir > .rp-tree-label:hover .rp-tree-name { text-decoration: underline; }
.rp-tree-toggle { display: inline-block; width: 1em; color: var(--rp-text-muted); }
.rp-tree-toggle.rp-tree-spacer { visibility: hidden; }
.rp-tree-label svg { width: 1.1em; height: 1.1em; flex: none; }
.rp-tree-name { font-family: var(--rp-mono); }

/* ---------------------------------------------------------------- tables */

/* The hosts table is wider than a 1280px viewport, and nothing wrapped it, so
   the whole page scrolled sideways and the last column fell off screen. The
   table now scrolls inside its own box and the page never does. */
.rp-table-wrap {
  background: var(--rp-surface);
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-radius);
  box-shadow: var(--rp-shadow);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.table {
  color: var(--rp-text);
  margin-bottom: 0;
  font-size: .875rem;
}
.table thead th {
  background: var(--rp-surface-2);
  border-bottom: 1px solid var(--rp-border) !important;
  border-top: 0 !important;
  color: var(--rp-text-muted);
  font-size: .75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
  padding: .6rem .75rem;
  white-space: nowrap;
}
.table td, .table th { border-top: 1px solid var(--rp-border); padding: .6rem .75rem; vertical-align: middle; }

/* Bulk-action bar occupying the header row while rows are selected --
   rpDataTableSelectionBar() in js/redpotato.js. Tinted rather than plain so
   "some of this table is selected" reads before the counter does. */
.rp-row-hidden { display: none; }
.table thead th.rp-select-cell {
  background: var(--rp-accent-soft);
  border-bottom: 1px solid var(--rp-accent) !important;
  color: var(--rp-text);
  font-size: .8125rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  /* No vertical padding: the row is sized to the header row it stands in
     for (see rpDataTableSelectionBar), and the controls have to fit inside
     that rather than add to it. */
  padding-top: 0;
  padding-bottom: 0;
}
.rp-select-cell > .rp-select-inner {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: center;
  height: 100%;
}
.table-bordered, .table-bordered td, .table-bordered th { border: 0; }
.table-bordered thead th { border-bottom: 1px solid var(--rp-border); }
.table tbody tr:first-child td { border-top: 0; }
.table-striped tbody tr:nth-of-type(odd) { background: transparent; }
.table-striped tbody tr:hover, .table tbody tr:hover { background: var(--rp-surface-2); }

/* A tooltip is not something anybody clicks, and letting it take the
   pointer is what made the certificate ones flicker: Bootstrap 4 does not
   set this itself, so a tooltip that lands over its own trigger -- which a
   tall one does whenever there is no room above it -- steals the hover, the
   trigger un-hovers, the tooltip hides, the trigger hovers again, for ever.
   Measured: a 170px tooltip on a badge 54px down the page was placed at
   top -92 and covered the badge it was describing. */
.tooltip { pointer-events: none; }

/* Picking rows by clicking or dragging across them -- rpDataTableRowSelect()
   in js/redpotato.js. The tint is what makes a drag legible: a dozen small
   checkboxes flicking on is not feedback anybody can follow at speed, and
   this is the same accent-soft the selection bar above uses, so "selected"
   looks like one idea in both places. It beats :hover on purpose -- the row
   under the pointer during a drag is selected, and showing it as merely
   hovered would be the wrong answer. */
.table tbody tr.rp-row-selected,
.table-striped tbody tr.rp-row-selected:hover,
.table tbody tr.rp-row-selected:hover { background: var(--rp-accent-soft); }

/* Only while a drag is actually under way. Dropping it on the table for
   good would cost double-click-to-select-a-word, which is how somebody
   copies an address out of here. */
.rp-dragselect, .rp-dragselect * { user-select: none; -webkit-user-select: none; }

/* Addresses, ports and tokens line up when they share an advance width */
.rp-mono, .table td.rp-mono { font-family: var(--rp-mono); font-variant-numeric: tabular-nums; }

.pnt { cursor: pointer; }
.truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.w50 { max-width: 100px; } .w100 { max-width: 100px; }
.w150 { max-width: 150px; } .w200 { max-width: 220px; }

/* ---------------------------------------------------------------- badges */

.badge {
  font-weight: 500;
  font-size: .75rem;
  padding: .3em .55em;
  border-radius: var(--rp-radius-sm);
  border: 1px solid transparent;
}
.badge-pill { border-radius: 10rem; }

.badge-success   { background: var(--rp-ok-soft);      color: var(--rp-ok);      border-color: var(--rp-ok); }
.badge-danger    { background: var(--rp-bad-soft);     color: var(--rp-bad);     border-color: var(--rp-bad); }
.badge-warning   { background: var(--rp-warn-soft);    color: var(--rp-warn);    border-color: var(--rp-warn); }
.badge-info      { background: var(--rp-info-soft);    color: var(--rp-info);    border-color: var(--rp-info); }
.badge-primary   { background: var(--rp-info-soft);    color: var(--rp-info);    border-color: var(--rp-info); }
.badge-secondary { background: var(--rp-neutral-soft); color: var(--rp-neutral); border-color: var(--rp-border-strong); }
.badge-light     { background: var(--rp-surface-2);    color: var(--rp-text-muted); border-color: var(--rp-border); }

/* A port badge is a link; keep the link colour off it */
a > .badge { text-decoration: none; }
a:hover > .badge { filter: brightness(.97); }

/* render_badge_list()'s "+N more": a host with hundreds of subdomains or
   ports used to make its own row taller than the rest of the table
   combined. Dashed border marks it as clickable, unlike a plain badge. */
.rp-badges-toggle { border-style: dashed; cursor: pointer; }
.rp-badges-toggle:hover { filter: brightness(.97); }

/* ---------------------------------------------------------------- buttons */

.btn {
  font-size: .8125rem;
  font-weight: 500;
  border-radius: var(--rp-radius-sm);
  padding: .4rem .8rem;
  border: 1px solid transparent;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease, transform .1s ease;
}
/* Presses register. Cheap, and it is the difference between a button that
   feels wired up and one that looks like it might not have taken the
   click -- most of this app's buttons fire an AJAX call whose result is
   a table refresh somewhere else on the page. */
.btn:active:not(:disabled) { transform: scale(.97); }
/* Bootstrap 4 predates :focus-visible: its own .btn:focus ring fires on a
   plain mouse click too. Dropped here, and replaced by the keyboard-only
   ring in the base rules above. */
.btn:focus { box-shadow: none; }

@media (prefers-reduced-motion: reduce) {
  .btn { transition: none; }
  .btn:active:not(:disabled) { transform: none; }
}
.btn-sm { font-size: .75rem; padding: .25rem .55rem; }

.btn-primary, .btn-success, .btn-info, .btn-secondary {
  background: var(--rp-surface);
  border-color: var(--rp-border-strong);
  color: var(--rp-text);
}
.btn-primary:hover, .btn-success:hover, .btn-info:hover, .btn-secondary:hover {
  background: var(--rp-surface-2);
  border-color: var(--rp-border-strong);
  color: var(--rp-text);
}
/* One primary action per toolbar, so it is obvious which one starts work */
.btn-primary {
  background: var(--rp-accent);
  border-color: var(--rp-accent);
  color: var(--rp-accent-text);
}
.btn-primary:hover, .btn-primary:focus {
  background: var(--rp-accent-hover);
  border-color: var(--rp-accent-hover);
  color: var(--rp-accent-text);
}
.btn-success { color: var(--rp-ok); border-color: var(--rp-ok); background: var(--rp-ok-soft); }
.btn-success:hover { background: var(--rp-ok); color: #fff; border-color: var(--rp-ok); }

.btn-outline-secondary { color: var(--rp-text-muted); border-color: var(--rp-border-strong); background: transparent; }
.btn-outline-secondary:hover { background: var(--rp-surface-2); color: var(--rp-text); border-color: var(--rp-border-strong); }
.btn-outline-danger { color: var(--rp-bad); border-color: var(--rp-bad); background: transparent; }
.btn-outline-danger:hover { background: var(--rp-bad); color: #fff; }
.btn-outline-primary { color: var(--rp-info); border-color: var(--rp-info); background: transparent; }
.btn-outline-primary:hover { background: var(--rp-info); color: #fff; }
.btn-outline-warning { color: var(--rp-warn); border-color: var(--rp-warn); background: transparent; }
.btn-outline-warning:hover { background: var(--rp-warn); color: #fff; }
.btn-outline-success { color: var(--rp-ok); border-color: var(--rp-ok); background: transparent; }
.btn-outline-success:hover { background: var(--rp-ok); color: #fff; }

.btn:focus, .btn.focus { box-shadow: 0 0 0 3px var(--rp-accent-soft); }

/* An icon-only row action (Scan/Delete on Domains and Hosts) needs its
   label in a tooltip, not repeated in every row -- the SVG just wants
   centering the label's own line-height used to give it for free. */
.btn .rp-nav-icon { display: block; margin: 0 auto; }

/* A toolbar button keeps its own label -- the icon sits inline to its
   left instead, not stacked above it the way a bare icon-only button's
   centering rule above would otherwise leave it. */
.rp-toolbar .btn .rp-nav-icon { display: inline-block; margin: 0 .35rem 0 0; vertical-align: -3px; }

/* Brief confirmation flash after a one-click action with no modal/redirect
   of its own to make the result obvious otherwise (queuing a scan, say). */
.rp-btn-flash, .rp-btn-flash:hover {
  background: var(--rp-ok-soft) !important;
  border-color: var(--rp-ok) !important;
  color: var(--rp-ok) !important;
  transition: background .15s ease, color .15s ease;
}

/* ---------------------------------------------------------------- forms */

.form-control, .custom-select {
  background: var(--rp-surface);
  border: 1px solid var(--rp-border-strong);
  border-radius: var(--rp-radius-sm);
  color: var(--rp-text);
  font-size: .875rem;
  height: auto;
  padding: .4rem .6rem;
}
.form-control:focus, .custom-select:focus {
  background: var(--rp-surface);
  border-color: var(--rp-accent);
  color: var(--rp-text);
  box-shadow: 0 0 0 3px var(--rp-accent-soft);
}
.form-control::placeholder { color: var(--rp-text-faint); }
select.form-control:not([size]):not([multiple]) { height: auto; }
label { color: var(--rp-text-muted); font-size: .8125rem; font-weight: 500; margin-bottom: .3rem; }
textarea.form-control { font-family: var(--rp-mono); font-size: .8125rem; }

/* ---------------------------------------------------------------- login */

.form-signin {
  background: var(--rp-surface);
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-radius);
  box-shadow: var(--rp-shadow-lg);
  max-width: 360px;
  margin: 4rem auto;
  padding: 2rem;
}
.form-signin h2 { font-size: 1.15rem; margin-bottom: 1.25rem; text-align: center; }
.form-signin input {
  background: var(--rp-surface-2);
  border: 1px solid var(--rp-border);
  color: var(--rp-text);
  width: 100%;
  margin: 0 0 .75rem;
  padding: .6rem .75rem;
  border-radius: var(--rp-radius-sm);
  font-size: .875rem;
  font-family: var(--rp-font);
  box-sizing: border-box;
}
.form-signin input:focus { outline: 0; border-color: var(--rp-accent); box-shadow: 0 0 0 3px var(--rp-accent-soft); }
.form-signin .btn { width: 100%; padding: .55rem; font-size: .875rem; }

/* ---------------------------------------------------------------- alerts */

.alert {
  border-radius: var(--rp-radius);
  border: 1px solid;
  font-size: .875rem;
  padding: .7rem 1rem;
}
.alert-success   { background: var(--rp-ok-soft);      border-color: var(--rp-ok);      color: var(--rp-ok); }
.alert-danger,
.alert-error     { background: var(--rp-bad-soft);     border-color: var(--rp-bad);     color: var(--rp-bad); }
.alert-warning   { background: var(--rp-warn-soft);    border-color: var(--rp-warn);    color: var(--rp-warn); }
.alert-info      { background: var(--rp-info-soft);    border-color: var(--rp-info);    color: var(--rp-info); }
.alert-secondary { background: var(--rp-surface-2);    border-color: var(--rp-border);  color: var(--rp-text-muted); }

/* ---------------------------------------------------------------- modals */

.modal-content {
  background: var(--rp-surface);
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-radius);
  box-shadow: var(--rp-shadow-lg);
  color: var(--rp-text);
}
.modal-header, .modal-footer { border-color: var(--rp-border); }
.modal-header { padding: .9rem 1.1rem; }
.modal-body { padding: 1.1rem; }
.modal-footer:empty { display: none; }
.modal-title { font-size: 1rem; font-weight: 600; }
.modal-header .close { color: var(--rp-text-muted); text-shadow: none; opacity: 1; }
.modal-header .close:hover { color: var(--rp-text); }
.modal-backdrop.show { opacity: .6; }

.dropdown-menu {
  background: var(--rp-surface);
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-radius);
  box-shadow: var(--rp-shadow-lg);
  font-size: .875rem;
}
.dropdown-item { color: var(--rp-text); padding: .4rem 1rem; }
.dropdown-item:hover, .dropdown-item:focus { background: var(--rp-surface-2); color: var(--rp-text); }

/* ---------------------------------------------------------------- DataTables */

.dataTables_wrapper { padding: 0; }
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_paginate {
  color: var(--rp-text-muted) !important;
  font-size: .8125rem;
  /* Level with the text in the first column. These controls sit in a Bootstrap
     row, whose -15px margin and 15px column padding cancel out against the
     inner edge of .rp-table-wrap, leaving them flush against its border while
     every cell in the table is inset by its own padding. Same figure as that
     cell padding, so "Showing 1 to 10" starts under the first column and the
     search box and pager keep off the right edge. */
  padding-left: .75rem;
  padding-right: .75rem;
}
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter { margin-bottom: .75rem; }
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_paginate { margin-top: .75rem; }

.dataTables_wrapper .dataTables_filter input,
.dataTables_wrapper .dataTables_length select {
  background: var(--rp-surface);
  border: 1px solid var(--rp-border-strong);
  border-radius: var(--rp-radius-sm);
  color: var(--rp-text);
  padding: .3rem .5rem;
}
/* The native select arrow is drawn by the OS in the light palette, which is
   invisible against a dark field, so it is drawn here instead. */
.dataTables_wrapper .dataTables_length select,
select.form-control {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='%23667085' d='M4.5 6.5 8 10l3.5-3.5z'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right .4rem center;
  background-size: 16px;
  padding-right: 1.6rem;
}
select[multiple].form-control { background-image: none; padding-right: .6rem; }
.dataTables_wrapper .dataTables_filter input:focus { outline: 0; border-color: var(--rp-accent); box-shadow: 0 0 0 3px var(--rp-accent-soft); }

/* This build of DataTables emits Bootstrap 4 pagination markup: an
   li.paginate_button.page-item wrapping an a.page-link. Both class names are
   present at once, on nested elements rather than as alternatives, so giving
   .paginate_button and .page-link the same border drew each button twice, one
   box inside the other.

   The anchor is the button; the item only carries it. A build that still emits
   the legacy bare .paginate_button anchor has no .page-item on it, which is
   what the :not() arm below is for. */
.dataTables_wrapper .dataTables_paginate li.paginate_button.page-item {
  background: transparent !important;
  border: 0 !important;
  padding: 0 !important;
  margin-left: .25rem;
}
.dataTables_wrapper .dataTables_paginate .paginate_button:not(.page-item),
.pagination .page-link {
  background: transparent !important;
  border: 1px solid var(--rp-border) !important;
  border-radius: var(--rp-radius-sm) !important;
  color: var(--rp-text-muted) !important;
  margin-left: .25rem;
  padding: .25rem .6rem;
  font-size: .8125rem;
}
.pagination .page-link { margin-left: 0; }
.dataTables_wrapper .dataTables_paginate .paginate_button:not(.page-item):hover,
.pagination .page-link:hover {
  background: var(--rp-surface-2) !important;
  color: var(--rp-text) !important;
  border-color: var(--rp-border-strong) !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button:not(.page-item).current,
.pagination .page-item.active .page-link {
  background: var(--rp-accent) !important;
  border-color: var(--rp-accent) !important;
  color: var(--rp-accent-text) !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button:not(.page-item).disabled,
.pagination .page-item.disabled .page-link {
  color: var(--rp-text-faint) !important;
  border-color: var(--rp-border) !important;
  background: transparent !important;
}
.pagination .page-link:focus { box-shadow: 0 0 0 3px var(--rp-accent-soft); }
table.dataTable thead .sorting,
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc { color: var(--rp-text-muted); }

/* ---------------------------------------------------------------- tooltips */

.tooltip-inner {
  background: var(--rp-text);
  color: var(--rp-bg);
  font-size: .75rem;
  border-radius: var(--rp-radius-sm);
  padding: .4rem .6rem;
  max-width: 320px;
  text-align: left;
}
.tooltip .arrow::before { border-right-color: var(--rp-text); border-left-color: var(--rp-text); }

/* ---------------------------------------------------------------- narrow screens */

@media (max-width: 767.98px) {
  .rp-main { padding-top: 3.25rem; }
  .rp-toolbar .btn { flex: 1 1 auto; }
  .rp-page-head { margin-bottom: 1rem; }
  /* Tables keep their own horizontal scroll rather than stretching the page */
  .table { font-size: .8125rem; }
  .table td, .table th { padding: .5rem .6rem; }

  /* Off canvas: the sidebar sits just off the left edge until opened, the
     content never shifts for it (there is no room to spare), and a small
     fixed button top-left is the only thing that pushes the page down.
     body.rp-sidebar-open is a separate, JS-only, never-persisted switch --
     the persisted data-sidebar="collapsed" is a desktop rail preference
     with nothing to do with whether the mobile drawer happens to be open,
     and a fresh load always starts with the drawer closed regardless of it. */
  .rp-sidebar {
    width: 260px;
    transform: translateX(-100%);
    box-shadow: var(--rp-shadow-lg);
  }
  body.rp-sidebar-open .rp-sidebar { transform: translateX(0); }
  .rp-content { margin-left: 0; }
  .rp-sidebar-collapse { display: none; }
  .rp-sidebar-close { display: inline-flex; }
  .rp-mobile-toggle { display: inline-flex; }
  body.rp-sidebar-open .rp-sidebar-backdrop { display: block; }
}

/* ---------------------------------------------------------------- screenshots */

/* A recon gallery is triaged by eye: a lot of thumbnails at once, each one
   readable enough to tell a login page from a default nginx page without
   opening it. Fixed aspect crop from the top, because what identifies a site
   is the header, not the footer. */
.rp-shots {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 1.25rem;
}

.rp-shot {
  margin: 0;
  background: var(--rp-surface);
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-radius);
  box-shadow: var(--rp-shadow);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.rp-shot > a {
  display: block;
  background: var(--rp-surface-2);
  border-bottom: 1px solid var(--rp-border);
  aspect-ratio: 4 / 3;
  overflow: hidden;
}
.rp-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
}
.rp-shot > a:hover img { opacity: .9; }
.rp-shot > a:focus-visible { outline: 2px solid var(--rp-accent); outline-offset: -2px; }

.rp-shot figcaption {
  padding: .6rem .75rem;
  display: grid;
  gap: .2rem;
  font-size: .8125rem;
  min-width: 0;
}
.rp-shot figcaption > * {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The status badge and the URL share one line of the caption; the URL is
   what gives way, since the badge is three characters and the URL can be a
   hundred. */
.rp-shot-url { display: flex; align-items: center; gap: .35rem; min-width: 0; }
.rp-shot-url > .badge { flex: 0 0 auto; }
.rp-shot-url > a { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* An icon's name sits above its hash: the name is what a person
   recognises, the hash is what a machine matches on. */
.rp-fav-name { display: flex; align-items: center; gap: .25rem; min-width: 0; }
.rp-fav-name > b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rp-fav-name .btn-link { line-height: 1; flex: 0 0 auto; }
.rp-fav-name .rp-nav-icon { width: 14px; height: 14px; }

/* The project chips on a favicon card wrap rather than being clipped: the
   whole point of the card is that an icon can be in several projects. */
.rp-fav-projects { white-space: normal; display: flex; flex-wrap: wrap; gap: .2rem; align-items: center; }

.rp-shot-banner { color: var(--rp-text-muted); }
.rp-shot-meta { color: var(--rp-text-faint); font-size: .75rem; }

/* A thumbnail next to a port row, small enough not to disturb the table */
.rp-shot-thumb {
  display: inline-block;
  width: 40px;
  height: 30px;
  border: 1px solid var(--rp-border);
  border-radius: var(--rp-radius-sm);
  overflow: hidden;
  vertical-align: middle;
  background: var(--rp-surface-2);
}
.rp-shot-thumb img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }

/* The Shodan queue's "What" cell: the domain being decided on, then the
   names under it. The domain leads because the domain IS the decision --
   one line is one new domain -- and the names below are the evidence for
   it. A slightly darker amber than Bootstrap's own .text-warning, which is
   unreadable on white at this size. */
.rp-intel-domain { margin-bottom: .15rem; }
.rp-intel-domain .text-warning-dark { color: #9a6700; }
.rp-intel-names { line-height: 1.9; }

/* The all-projects overview table (home page and project list). One row per
   project, so everything that is a stacked block on the Dashboard has to sit
   on a single line here without changing what it means. */
.rp-overview td { vertical-align: middle; }
/* Nothing in a row wraps. Every cell here is a short label, a count or a
   group of badges, and letting them wrap gives each project a different row
   height -- which is what a table scanned top to bottom cannot afford. The
   .rp-table-wrap around it scrolls sideways instead, which the wide tables
   in this application already do. */
.rp-overview th, .rp-overview td { white-space: nowrap; }
/* Tighter than the default table cell: this one carries nine or ten columns
   and the last of them on the project list is the Archive button, which has
   to be reachable without scrolling sideways on an ordinary screen. */
.rp-overview th, .rp-overview td { padding-left: .55rem; padding-right: .55rem; }
/* The sparkline and its current value side by side rather than stacked, at
   text size -- the Dashboard's own .rp-trend-value is a 1.6rem headline
   number, which would set the height of every row in the table. */
.rp-overview-trend { display: flex; align-items: center; gap: .45rem; }
.rp-overview-trend .rp-spark { color: var(--rp-accent); flex: 0 0 auto; }
.rp-overview-trend .rp-trend-value { font-size: .9rem; font-weight: 600; }
/* A group of badges wrapped in one link to the page that answers them. The
   link must not colour or underline them: the badge colours are the
   severity scale, and an underline under three badges reads as a mistake. */
a.rp-badge-link, a.rp-badge-link:hover, a.rp-badge-link:focus { text-decoration: none; }
