/* ---- theme tokens: components read these, dark/light flips them ---- */
:root {
  --bg: #ffffff;
  --surface: #ffffff;
  --surface-alt: #f4ede7;
  --banner: #FFFCE2;
  --text: #212529;
  --border: #d7dde5;
  --link: #0b5ed7;
  --nav-bg: #033c73;
  --nav-border: #022f5a;
  --nav-text: #ffffff;
  --input-bg: #ffffff;
  --input-text: #212529;
  --kbd-bg: #f0f0f0;
  --shadow: rgba(0, 0, 0, 0.18);
  --muted: #5d6673;
  /* the mapmyvisitors widget paints an opaque ocean; keep it in one place */
  --widget-ocean: #2878a8;
}
html.dark {
  --bg: #12161c;
  --surface: #1a212b;
  --surface-alt: #161d26;
  --banner: #26231a;
  --text: #d7dee8;
  --border: #2a3340;
  --link: #86b6ff;
  --nav-bg: #161b23;
  --nav-border: #262e3a;
  --nav-text: #c9d3df;
  --input-bg: #131922;
  --input-text: #e8edf4;
  --kbd-bg: #131922;
  --shadow: rgba(0, 0, 0, 0.45);
  --muted: #aeb9c9;
  --widget-ocean: #236a94;
}
.navbar-inverse {
  background-color: var(--nav-bg);
  border-bottom: 1px solid var(--nav-border);
}
.list-group-item{
	font-size:12px;
}
p.search {
line-height: 16px;
padding-top: 10px;
padding-left: 6px;
}

.panel {
	background-color: var(--surface-alt);
}

/* 12px, matching the framework's own gutter. With 5px the row's -12px margins pushed
   every page 7px wider than the viewport on a 400px screen. */
.container {
  margin-right: auto;
  margin-left: auto;
  padding-left: 12px;
  padding-right: 12px;
}

.has-search .form-control {
  padding-left: 2.375rem;
}

.has-search .form-control-feedback {
  position: absolute;
  z-index: 2;
  display: block;
  width: 2.375rem;
  height: 2.375rem;
  line-height: 2.375rem;
  text-align: center;
  pointer-events: none;
  color: #aaa;
}

body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* ---- themed components ---- */
html.dark {
  color-scheme: dark;
}
body {
  background-color: var(--bg);
  color: var(--text);
}
.card,
.list-group-item {
  background-color: var(--surface);
  border-color: var(--border);
  color: var(--text);
}
.info-top {
  background-color: var(--banner);
  border: 1px solid var(--border);
  padding: 10px;
}
.form-control {
  background-color: var(--input-bg);
  border-color: var(--border);
  color: var(--input-text);
}
hr {
  border-color: var(--border);
}
a {
  color: var(--link);
}
.navbar-brand,
.nav-link {
  color: var(--nav-text) !important;
}

.theme-toggle,
.help-toggle {
  background: transparent;
  border: none;
  color: var(--nav-text);
  cursor: pointer;
  font-size: 24px;
  line-height: 1;
  padding: 4px 6px;
  vertical-align: middle;
}

/* ---- top bar menus: Bootstrap's own dropdown, themed from our tokens -------
   The Tools / Games / Language menus are plain Bootstrap 5 dropdowns
   (`.dropdown` / `.dropdown-menu` / `.dropdown-item`) driven by
   bootstrap.bundle.min.js, which is loaded on every page that includes the bar and
   already contains Popper — so there is no bespoke menu CSS or JS left in the
   template. Bootstrap reads these `--bs-dropdown-*` variables when it paints a
   panel, so pointing them at our tokens is the whole theme: the panel is
   `--surface` (white in light, #1a212b in dark) with `--text` on it, and the
   language you are reading gets a hair of background and a tick. The hand-rolled
   panels this replaced hard-coded `#12181f`, so they were a black box in the light
   theme as well as the dark one — the bug this fixes. */
.navbar-inverse .dropdown-menu {
  --bs-dropdown-bg: var(--surface);
  --bs-dropdown-color: var(--text);
  --bs-dropdown-border-color: var(--border);
  --bs-dropdown-link-color: var(--text);
  --bs-dropdown-link-hover-color: var(--text);
  --bs-dropdown-link-hover-bg: var(--surface-alt);
  --bs-dropdown-link-active-color: var(--text);
  --bs-dropdown-link-active-bg: var(--surface-alt);
  --bs-dropdown-link-disabled-color: var(--muted);
  --bs-dropdown-box-shadow: 0 12px 34px var(--shadow);
  --bs-dropdown-min-width: 268px;
  --bs-dropdown-font-size: .875rem;
  --bs-dropdown-item-padding-x: 1rem;
  --bs-dropdown-item-padding-y: .5rem;
  /* the Tools panel carries 17 items: let it scroll rather than run off a short screen */
  max-height: min(80vh, 36rem);
  overflow-y: auto;
}
.navbar-inverse .dropdown-item img {
  margin-right: 9px;
  vertical-align: middle;
}
.navbar-inverse .dropdown-item[aria-current="true"] {
  background-color: var(--surface-alt);
}
.navbar-inverse .dropdown-item[aria-current="true"]::after {
  content: "\2713";
  float: right;
  margin-left: 14px;
}

/* ---- Tools mega menu: three titled columns ---------------------------------
   Tools carries 17 destinations, which was one long scrolling list. Grouping them
   into titled columns makes it short and scannable. Bootstrap has no submenu
   component, so the columns are plain flexbox over Bootstrap's own
   `.dropdown-header` / `.dropdown-item` markup — no JS. `display:flex` applies only
   while `.show`, or it would defeat Bootstrap's `display:none` on a closed menu.
   Columns are a >=992px affordance (navbar-expand-lg); the collapsed phone menu
   falls back to one stacked column in the media query below. */
.navbar-inverse .dropdown-menu.ipmap-tools-menu.show {
  display: flex;
  gap: 10px;
  padding: 10px;
  /* three columns want ~940px, but the panel starts ~136px in from the left, so at
     the 992px lg breakpoint that would clip. Cap it to the viewport; the columns
     then share the width and a long label wraps instead of running off screen. */
  width: min(900px, calc(100vw - 160px));
}
.ipmap-tools-col {
  flex: 1 1 0;
  min-width: 0;
}
.ipmap-tools-menu .dropdown-item {
  white-space: normal;
}
.ipmap-tools-col + .ipmap-tools-col {
  border-left: 1px solid var(--border);
  padding-left: 10px;
}
.ipmap-tools-menu .dropdown-header {
  padding: .25rem 1rem .375rem;
  color: var(--muted);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}
@media (max-width: 991.98px) {
  .navbar-inverse .dropdown-menu.ipmap-tools-menu.show {
    display: block;
    width: auto;
    padding: .5rem 0;
  }
  .ipmap-tools-col + .ipmap-tools-col {
    border-left: 0;
    padding-left: 0;
    margin-top: 6px;
  }
}

.shortcut-help {
  display: none;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1050;
  max-width: calc(100vw - 32px);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 18px 24px;
  font-size: 16px;
  line-height: 1.5;
  box-shadow: 0 10px 40px var(--shadow);
}
.shortcut-help div {
  margin: 6px 0;
}
.shortcut-help kbd {
  background: var(--kbd-bg);
  /* Bootstrap 4 sets kbd { color: #fff }; without this the keys vanish on light theme */
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 2px 9px;
  margin-right: 10px;
  font-family: monospace;
  font-size: 15px;
}
.shortcut-help-title {
  font-weight: bold;
  font-size: 18px;
  margin-bottom: 10px;
}

/* ---- polish ---- */
.card,
.list-group-item,
.form-control,
.navbar {
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.card {
  border-radius: 10px;
  box-shadow: 0 1px 3px var(--shadow);
}
:focus-visible {
  outline: 2px solid var(--link);
  outline-offset: 2px;
}

/* ---- footer: recent lookups + live visitor map ---- */
.site-footer {
  margin-top: 24px;
}
.footer-card {
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 1px 3px var(--shadow);
  padding: 16px;
}
.footer-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}
.footer-head-icon {
  font-size: 24px;
  line-height: 1;
  color: var(--link);
  background-color: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 9px;
  padding: 6px;
}
.footer-title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 600;
}
.footer-chip {
  margin-left: auto;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  background-color: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 10px;
}

/* the tracker image is an opaque blue rectangle: frame it as a deliberate card */
.visit-map {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* the list column is usually a little taller: keep the captions on the same
   baseline instead of leaving a ragged gap under the map */
@media (min-width: 1200px) {
  .visit-map {
    height: 100%;
    justify-content: space-between;
  }
}
.visit-map-link {
  position: relative;
  display: block;
  padding: 6px;
  border-radius: 12px;
  overflow: hidden;
  background-color: var(--widget-ocean);
  border: 1px solid var(--border);
  box-shadow: 0 6px 18px var(--shadow);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.visit-map-link:hover,
.visit-map-link:focus-visible {
  transform: translateY(-2px);
  border-color: var(--link);
  box-shadow: 0 12px 28px var(--shadow);
}
.visit-map-link img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 7px;
}
html.dark .visit-map-link img {
  filter: brightness(0.88) saturate(0.95);
}
/* the tracker prints its own counter in the top-right corner, so the caption
   carries the label instead of an overlay that would sit on top of it */
.visit-map-caption {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.8rem;
  color: var(--muted);
}
.visit-map-caption .material-symbols-rounded {
  font-size: 17px;
  color: var(--link);
}
/* tracker blocked (ad/script blocker): keep the panel, drop the broken image */
.visit-map-link.visit-map-failed {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px 14px;
}
.visit-map-link.visit-map-failed img {
  display: none;
}
.visit-map-failed::after {
  content: "Visitor map is unavailable.";
  color: #fff;
  font-size: 13px;
  opacity: 0.9;
}

/* one compact row per lookup: IP left, country/city right */
.recent-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.recent-item a {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  font-size: 13px;
  line-height: 1.35;
  border: 1px solid var(--border);
  border-radius: 10px;
  background-color: var(--surface-alt);
  color: var(--text);
  text-decoration: none;
  transition: border-color 0.15s ease, transform 0.15s ease, background-color 0.15s ease;
}
.recent-item a:hover,
.recent-item a:focus-visible {
  border-color: var(--link);
  transform: translateX(2px);
  text-decoration: none;
}
.recent-pin {
  font-size: 17px;
  color: var(--link);
}
.recent-ip {
  flex: 0 0 auto;
  min-width: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.recent-city {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12px;
  color: var(--muted);
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.recent-go {
  font-size: 17px;
  color: var(--muted);
}
.recent-list,
.footer-card,
.visit-map-link {
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
@media (max-width: 1199.98px) {
  .visit-map {
    margin-bottom: 16px;
  }
}
/* narrow screens: give the location its own line so nothing is cut off */
@media (max-width: 575.98px) {
  .recent-item a {
    flex-wrap: wrap;
  }
  .recent-ip {
    flex: 1 1 auto;
  }
  .recent-city {
    flex: 1 0 100%;
    padding-left: 25px;
    text-align: left;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
  }
  .recent-go {
    display: none;
  }
}

/* ---- IP page: step to the previous / next address ---- */
.ip-nav {
  display: flex;
  align-items: center;
  gap: 8px;
}
.ip-nav h1 {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}
.ip-step {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--surface-alt);
  color: var(--text);
  text-decoration: none;
  transition: transform .15s ease, background-color .15s ease, border-color .15s ease, color .15s ease;
}
.ip-step .material-symbols-rounded {
  font-size: 22px;
}
.ip-step:hover,
.ip-step:focus-visible {
  background: var(--link);
  border-color: var(--link);
  color: var(--bg);
  transform: translateY(-1px);
}
.ip-step:focus-visible {
  outline: 2px solid var(--link);
  outline-offset: 2px;
}
.ip-step.is-disabled {
  opacity: .38;
  cursor: not-allowed;
  background: transparent;
  border-style: dashed;
}
.ip-step.is-disabled:hover {
  transform: none;
  background: transparent;
  border-color: var(--border);
  color: var(--text);
}
@media (max-width: 575.98px) {
  .ip-nav {
    gap: 6px;
  }
  .ip-step {
    width: 30px;
    height: 30px;
  }
  .ip-step .material-symbols-rounded {
    font-size: 20px;
  }
}

/* ---- top bar: step to the previous / next address (IP pages only) ---- */
.nav-steps {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-right: 10px;
}
.nav-step {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, .28);
  color: var(--nav-text);
  font-size: 12.5px;
  line-height: 1.5;
  text-decoration: none;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.nav-step .material-symbols-rounded {
  font-size: 18px;
}
.nav-step-ip {
  /* an IPv6 address would blow the bar apart: ellipsis, full value in the tooltip */
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.nav-step:hover,
.nav-step:focus-visible {
  background: rgba(255, 255, 255, .16);
  border-color: rgba(255, 255, 255, .5);
  color: #fff;
  text-decoration: none;
}
.nav-step:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* ---- Bootstrap 5.3.3 compatibility --------------------------------------
   Only for Bootstrap 4 names this app still uses that Bootstrap 5 removed.
   `.text-muted` is the renamed `.text-body-secondary`; keep the old name so
   the templates (76 uses) do not have to change. `.panel`, `.navbar-inverse`
   and `.form-control-feedback` are the app's own classes and stay defined
   above, not here. */
.text-muted {
  /* OUR token, not Bootstrap's `--bs-secondary-color`: this app themes with
     `html.dark` (set from prefers-color-scheme in headers.html.j2), NOT with
     `data-bs-theme="dark"`, so Bootstrap's variable never flips — every muted
     string was dark grey on the dark background. */
  color: var(--muted) !important;
}

/* ---- the map's place label ----------------------------------------------
   The pin's label is a MapLibre popup, and maplibre-gl.css hard-codes the card
   to `background:#fff` while setting NO colour — so in dark mode the text
   inherited the page's pale `--text` and rendered light grey on white, which is
   how a visitor found it (illegible, and a white hole punched in a dark map).
   Both halves are set here from our own tokens, so the card follows `html.dark`
   like everything else. The tip is border-drawn: only the visible side may be
   coloured, or the triangle becomes a square. */
.maplibregl-popup.ipmap-map-label .maplibregl-popup-content {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
  font-size: 0.8125rem;
  line-height: 1.25;
  max-width: 15rem;
  padding: 0.3rem 0.55rem;
}
.maplibregl-popup.ipmap-map-label[class*="anchor-top"] .maplibregl-popup-tip {
  border-bottom-color: var(--surface);
}
.maplibregl-popup.ipmap-map-label[class*="anchor-bottom"] .maplibregl-popup-tip {
  border-top-color: var(--surface);
}
.maplibregl-popup.ipmap-map-label[class*="anchor-left"] .maplibregl-popup-tip {
  border-right-color: var(--surface);
}
.maplibregl-popup.ipmap-map-label[class*="anchor-right"] .maplibregl-popup-tip {
  border-left-color: var(--surface);
}

/* ---- the same label on the Leaflet fallback ------------------------------
   Identical treatment for the other provider's tooltip (IPMAP_MAP_PROVIDER=openstreetmap), so a
   provider switch is not a visual regression: Leaflet ships a white tooltip with `color:#222` — which
   is legible, unlike MapLibre's — but a white card on a dark page still reads as a foreign element.
   Its arrow is border-drawn like MapLibre's: only the visible side may be coloured. */
.leaflet-tooltip.ipmap-map-label {
  background-color: var(--surface);
  border-color: var(--border);
  color: var(--text);
  border-radius: 6px;
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
  font-size: 0.8125rem;
  line-height: 1.25;
  max-width: 15rem;
  white-space: normal;
  padding: 0.3rem 0.55rem;
}
.leaflet-tooltip-top.ipmap-map-label:before {
  border-top-color: var(--surface);
}
.leaflet-tooltip-bottom.ipmap-map-label:before {
  border-bottom-color: var(--surface);
}
.leaflet-tooltip-left.ipmap-map-label:before {
  border-left-color: var(--surface);
}
.leaflet-tooltip-right.ipmap-map-label:before {
  border-right-color: var(--surface);
}

/* ---- inline loading state ------------------------------------------------
   For the AJAX rows that say "Checking…" while a guarded *.htm endpoint answers
   (history, DNS, HTTP headers, TLS, visibility). A GIF would need a light and a
   dark copy and one more request; this is drawn from the text colour it sits in,
   scales with the font size, and stops moving for anyone who asked for less
   motion. Put the class on the element that holds the text — the disc is a
   ::before, so the markup stays one plain paragraph. */
.ipmap-loading {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--muted);
  margin: 0;
}
.ipmap-loading::before {
  content: "";
  flex: none;
  width: 1em;
  height: 1em;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  /* full strength: at 0.85 the ring washed out on white, while on the dark
     theme it looked right — one opacity cannot serve both */
  opacity: 1;
  animation: ipmap-spin 0.8s linear infinite;
}
@keyframes ipmap-spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .ipmap-loading::before {
    animation-duration: 2.4s;
  }
}
