/*
 * Loaded after bootstrap.min.css: keep selectors identical to Bootstrap's so overrides win
 * by cascade order. Variant colors are hex literals compiled into component variables
 * (.btn-primary{--bs-btn-bg:#0d6efd}), so changing --bs-primary does nothing — redefine
 * those variables. Token values come from vercel.com's --ds-* properties (docs/ui.md).
 */

@font-face {
  font-family: "Ubuntu";
  src: url("/assets/ubuntu-latin-400-normal-c1bb3829.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Ubuntu";
  src: url("/assets/ubuntu-latin-500-normal-7185e354.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}

@font-face {
  font-family: "Ubuntu";
  src: url("/assets/ubuntu-latin-700-normal-acb7d6b6.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

/* Ubuntu has no 600; .fw-semibold (600) lands on the 700 face by font matching. */

@font-face {
  font-family: "Ubuntu Mono";
  src: url("/assets/ubuntu-mono-latin-400-normal-f2bda096.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Ubuntu Mono";
  src: url("/assets/ubuntu-mono-latin-700-normal-9316c6c1.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

:root,
[data-bs-theme="light"] {
  /* Bootstrap's CSS only ships `color-scheme: dark` (inside its own dark block), so
     light has to be declared here or native controls and scrollbars follow the OS. */
  color-scheme: light;

  /* Geist tokens — light */
  --ds-background-100: #fff;
  --ds-background-200: #fafafa;
  --ds-gray-100: #f2f2f2;
  --ds-gray-200: #ebebeb;
  --ds-gray-300: #e6e6e6;
  --ds-gray-400: #eaeaea;
  --ds-gray-500: #c9c9c9;
  --ds-gray-600: #a8a8a8;
  --ds-gray-700: #8f8f8f;
  --ds-gray-800: #7d7d7d;
  --ds-gray-900: #4d4d4d;
  --ds-gray-1000: #171717;
  --ds-gray-alpha-400: rgba(0, 0, 0, 0.08);
  --ds-gray-alpha-600: rgba(0, 0, 0, 0.24);
  --ds-blue-700: #0070f7;
  --ds-red-100: #ffeef0;
  --ds-red-400: #ffd8d7;
  --ds-red-800: #e70022;
  --ds-red-900: #d60020;
  --ds-red-1000: #46000c;
  --ds-amber-100: #fff6e1;
  --ds-amber-400: #ffdd84;
  --ds-amber-1000: #541c00;
  --ds-green-100: #ecfdec;
  --ds-green-400: #b9f5bc;
  --ds-green-800: #279141;
  --ds-green-1000: #00370d;
  --ds-focus-ring: rgba(0, 0, 0, 0.16);
  --ds-focus-border: 0 0 0 1px var(--ds-gray-alpha-600), 0 0 0 4px var(--ds-focus-ring);
  --ds-radius: 6px;
  /* Primary button under the cursor and while pressed: Vercel lifts black by one step,
     then goes all the way. Dark mode inverts the button, so both flip with the theme. */
  --ds-primary-hover: #383838;
  --ds-primary-active: #000;
  /* Translucent, so the blurred page shows through under the sticky navbar. */
  --ds-navbar-bg: rgba(255, 255, 255, 0.8);
  --ds-font-sans-fallback: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --ds-font-mono-fallback: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;

  /* Bootstrap mapped onto the tokens above */
  --bs-body-font-family: "Ubuntu", var(--ds-font-sans-fallback);
  /* Bootstrap points code, kbd, pre, samp and .font-monospace at this one. */
  --bs-font-monospace: "Ubuntu Mono", var(--ds-font-mono-fallback);
  --bs-body-color: var(--ds-gray-1000);
  --bs-body-color-rgb: 23, 23, 23;
  --bs-body-bg: var(--ds-background-100);
  --bs-body-bg-rgb: 255, 255, 255;
  --bs-body-font-weight: 400;
  --bs-emphasis-color: var(--ds-gray-1000);
  --bs-secondary-color: var(--ds-gray-900);
  --bs-tertiary-bg: var(--ds-gray-100);
  --bs-tertiary-bg-rgb: 242, 242, 242;
  --bs-border-color: var(--ds-gray-alpha-400);
  --bs-border-color-translucent: var(--ds-gray-alpha-400);
  --bs-border-radius: var(--ds-radius);
  --bs-border-radius-sm: 4px;
  --bs-border-radius-lg: 8px;
  --bs-link-color: var(--ds-gray-1000);
  --bs-link-hover-color: var(--ds-gray-900);
  /* Components read the *-rgb triples, not the hex values, so overriding only the hex
     forms leaves links and badges blue; both forms are set here. */
  --bs-primary: var(--ds-gray-1000);
  --bs-primary-rgb: 23, 23, 23;
  --bs-primary-text-emphasis: var(--ds-gray-1000);
  --bs-primary-bg-subtle: var(--ds-gray-100);
  --bs-primary-border-subtle: var(--ds-gray-300);
  --bs-link-color-rgb: 23, 23, 23;
  --bs-link-hover-color-rgb: 77, 77, 77;
  --bs-secondary-rgb: 77, 77, 77;
  --bs-focus-ring-color: var(--ds-focus-ring);
  --bs-form-invalid-color: var(--ds-red-800);
  --bs-form-invalid-border-color: var(--ds-red-800);
  --bs-form-valid-color: var(--ds-green-800);
  --bs-form-valid-border-color: var(--ds-green-800);
}

/* Geist tokens — dark. Bootstrap's own dark palette loses to the `:root` block above
   (equal specificity, this file loads last), so only the literals need restating. */
[data-bs-theme="dark"] {
  color-scheme: dark;

  --ds-background-100: #000;
  --ds-background-200: #000;
  --ds-gray-100: #1a1a1a;
  --ds-gray-200: #1f1f1f;
  --ds-gray-300: #292929;
  --ds-gray-400: #2e2e2e;
  --ds-gray-500: #454545;
  --ds-gray-600: #878787;
  --ds-gray-700: #8f8f8f;
  --ds-gray-800: #7d7d7d;
  --ds-gray-900: #a0a0a0;
  --ds-gray-1000: #ededed;
  --ds-gray-alpha-400: rgba(255, 255, 255, 0.14);
  --ds-gray-alpha-600: rgba(255, 255, 255, 0.51);
  --ds-blue-700: #0071f6;
  --ds-red-100: #330a11;
  --ds-red-400: #6f101b;
  --ds-red-800: #e2162a;
  --ds-red-900: #ff5e63;
  --ds-red-1000: #ffeaed;
  --ds-amber-100: #291800;
  --ds-amber-400: #573200;
  --ds-amber-1000: #fff3d9;
  --ds-green-100: #00250a;
  --ds-green-400: #004616;
  --ds-green-800: #009335;
  --ds-green-1000: #daffe5;
  --ds-focus-ring: rgba(255, 255, 255, 0.16);
  --ds-primary-hover: #cccccc;
  --ds-primary-active: #fff;
  --ds-navbar-bg: rgba(0, 0, 0, 0.8);

  /* Bootstrap mapped onto the tokens above — the literals only */
  --bs-body-color-rgb: 237, 237, 237;
  --bs-body-bg-rgb: 0, 0, 0;
  --bs-primary-rgb: 237, 237, 237;
  --bs-link-color-rgb: 237, 237, 237;
  --bs-link-hover-color-rgb: 160, 160, 160;
  --bs-secondary-rgb: 160, 160, 160;
  --bs-tertiary-bg-rgb: 26, 26, 26;
}

body {
  -webkit-font-smoothing: antialiased;
}

/* Vercel sets its headings tight. */
h1, h2, h3, h4 {
  letter-spacing: -0.02em;
}

/* Buttons: flat, 14px, black primary. */
.btn {
  --bs-btn-font-size: 0.875rem;
  --bs-btn-font-weight: 500;
  --bs-btn-padding-x: 0.875rem;
  --bs-btn-padding-y: 0.375rem;
  --bs-btn-box-shadow: none;
  --bs-btn-focus-box-shadow: 0 0 0 4px rgba(0, 112, 247, 0.25);
}

.btn-primary {
  --bs-btn-color: var(--ds-background-100);
  --bs-btn-bg: var(--ds-gray-1000);
  --bs-btn-border-color: var(--ds-gray-1000);
  --bs-btn-hover-color: var(--ds-background-100);
  --bs-btn-hover-bg: var(--ds-primary-hover);
  --bs-btn-hover-border-color: var(--ds-primary-hover);
  --bs-btn-active-color: var(--ds-background-100);
  --bs-btn-active-bg: var(--ds-primary-active);
  --bs-btn-active-border-color: var(--ds-primary-active);
  --bs-btn-disabled-color: var(--ds-background-100);
  --bs-btn-disabled-bg: var(--ds-gray-1000);
  --bs-btn-disabled-border-color: var(--ds-gray-1000);
}

/* Dark mode's pressed (and hovered) red is near-white, so the label flips to black to
   stay readable — in light mode both fills are dark and keep Bootstrap's white. */
[data-bs-theme="dark"] .btn-danger {
  --bs-btn-hover-color: #000;
  --bs-btn-active-color: #000;
}

.btn-danger {
  --bs-btn-bg: var(--ds-red-800);
  --bs-btn-border-color: var(--ds-red-800);
  --bs-btn-hover-bg: var(--ds-red-900);
  --bs-btn-hover-border-color: var(--ds-red-900);
  --bs-btn-active-bg: var(--ds-red-1000);
  --bs-btn-active-border-color: var(--ds-red-1000);
}

.btn-outline-secondary {
  --bs-btn-color: var(--ds-gray-1000);
  --bs-btn-border-color: var(--ds-gray-alpha-400);
  --bs-btn-hover-color: var(--ds-gray-1000);
  --bs-btn-hover-bg: var(--ds-gray-100);
  --bs-btn-hover-border-color: var(--ds-gray-alpha-400);
  --bs-btn-active-color: var(--ds-gray-1000);
  --bs-btn-active-bg: var(--ds-gray-200);
  --bs-btn-active-border-color: var(--ds-gray-300);
  --bs-btn-disabled-color: var(--ds-gray-700);
  --bs-btn-disabled-border-color: var(--ds-gray-300);
}

/* Badges: Vercel tints a light background with dark text; Bootstrap's .text-bg-* is a solid
   fill with white text, so these roles carry the colors instead. */
/* Bootstrap's badge in Vercel's tag shape. Every knob is a variable because the variants
   below set nothing but colour. */
.badge {
  --bs-badge-font-weight: 500;
  --bs-badge-font-size: 0.75rem;
  --bs-badge-padding-x: 0.5rem;
  --bs-badge-padding-y: 0.25rem;
  --bs-badge-border-radius: 9999px;
}

.badge-primary {
  background-color: var(--ds-gray-1000);
  color: var(--ds-background-100);
}

/* The label shape: gray-1000 on gray-100, Vercel's gray-tag contrast. */
.badge-neutral {
  background-color: var(--ds-gray-100);
  color: var(--ds-gray-1000);
}

.badge-success {
  background-color: var(--ds-green-100);
  color: var(--ds-green-1000);
}

.badge-danger {
  background-color: var(--ds-red-100);
  color: var(--ds-red-1000);
}

.badge-attention {
  background-color: var(--ds-amber-100);
  color: var(--ds-amber-1000);
}

/* Forms: Vercel's focus is a hairline black ring, not Bootstrap's blue glow. */
.form-label {
  font-size: 0.875rem;
  font-weight: 500;
}

.form-text {
  font-size: 0.8125rem;
}

.form-control:focus,
.form-select:focus {
  border-color: var(--ds-gray-alpha-600);
  box-shadow: var(--ds-focus-border);
}

.form-check-input:checked,
.form-check-input[type="checkbox"]:indeterminate {
  background-color: var(--ds-gray-1000);
  border-color: var(--ds-gray-1000);
}

.form-check-input:focus {
  border-color: var(--ds-gray-alpha-600);
  box-shadow: var(--ds-focus-border);
}

.alert {
  --bs-alert-border-radius: var(--ds-radius);
  --bs-alert-padding-y: 0.75rem;
}

.alert-danger {
  --bs-alert-color: var(--ds-red-1000);
  --bs-alert-bg: var(--ds-red-100);
  --bs-alert-border-color: var(--ds-red-400);
}

.alert-success {
  --bs-alert-color: var(--ds-green-1000);
  --bs-alert-bg: var(--ds-green-100);
  --bs-alert-border-color: var(--ds-green-400);
}

/* Pagination: borderless, gray hover. */
.pagination {
  --bs-pagination-border-width: 0;
  --bs-pagination-color: var(--ds-gray-1000);
  --bs-pagination-hover-color: var(--ds-gray-1000);
  --bs-pagination-hover-bg: var(--ds-gray-100);
  --bs-pagination-active-color: var(--ds-background-100);
  --bs-pagination-active-bg: var(--ds-gray-1000);
  --bs-pagination-disabled-color: var(--ds-gray-900); /* gray-700 is 3.2:1 on white */
  --bs-pagination-disabled-bg: transparent;
  --bs-pagination-focus-box-shadow: var(--ds-focus-border);
  gap: 0.25rem;
}

/* Bootstrap compiles #0d6efd straight into --bs-dropdown-link-active-bg, so the active
   item stays blue until that variable is redefined. */
.dropdown-menu {
  --bs-dropdown-font-size: 0.875rem;
  --bs-dropdown-link-active-color: var(--ds-background-100);
  --bs-dropdown-link-active-bg: var(--ds-gray-1000);
}

/* The issue feedback menu's "Something else" box (issues/_feedback_menu) sits inside the
   dropdown, whose width is its longest item; a sentence needs more room than that. */
.issue-feedback__note {
  min-width: 18rem;
}

/* Navbar: Vercel's is 64px tall, translucent, with a hairline bottom border. */
.navbar {
  --bs-navbar-padding-y: 0;
  --bs-navbar-color: var(--ds-gray-900);
  --bs-navbar-hover-color: var(--ds-gray-1000);
  --bs-navbar-active-color: var(--ds-gray-1000);
  --bs-navbar-brand-color: var(--ds-gray-1000);
  --bs-navbar-brand-hover-color: var(--ds-gray-1000);
  --bs-navbar-toggler-border-color: var(--ds-gray-alpha-400);
  --bs-navbar-toggler-padding-x: 0.5rem;
  --bs-navbar-toggler-focus-width: 0;
  --bs-navbar-nav-link-padding-x: 0.75rem;
}

/* Bootstrap hard-codes a blue keyboard-focus ring on nav links; everywhere else the
   theme uses the hairline black one. */
.nav-link:focus-visible {
  box-shadow: var(--ds-focus-border);
}

.site-navbar {
  background-color: var(--ds-navbar-bg);
  backdrop-filter: saturate(180%) blur(5px);
  border-bottom: 1px solid var(--ds-gray-alpha-400);
}

/* The 64px is a min-height on the row, not the <header>: Bootstrap's align-items then
   centers it, and it still grows when the mobile menu expands. */
.site-navbar .navbar {
  min-height: 64px;
  /* `.navbar`'s padding shorthand (x defaults to 0) beats the container's gutter on this
     shared element, so the half-gutter is restored explicitly. */
  --bs-navbar-padding-x: calc(var(--bs-gutter-x) * 0.5);
}

/* Block, so the link is exactly the logo's height; never shrunk, so a narrow phone
   squeezes the gap rather than the wordmark. */
.site-logo {
  display: block;
  flex-shrink: 0;
}

/* 24px so the trigger stays the height of the links beside it. */
.nav-avatar {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background-color: var(--ds-gray-alpha-400);
}

/* The home page's technology tiles: the whole card is the link target. */
.topic-tile {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 88px;
  padding: 1rem;
  border: 1px solid var(--ds-gray-alpha-400);
  border-radius: var(--ds-radius);
  color: var(--ds-gray-1000);
  font-weight: 500;
  text-decoration: none;
}

.topic-tile:hover {
  background-color: var(--ds-gray-100);
  color: var(--ds-gray-1000);
}

.topic-tile:focus-visible {
  outline: none;
  box-shadow: var(--ds-focus-border);
}

/* The Hacktoberfest hero and campaign banner share the theme's amber, which the dark
   block already restates. */
.event-hero {
  background-color: var(--ds-amber-100);
  border: 1px solid var(--ds-amber-400);
}

.campaign-banner {
  background-color: var(--ds-amber-100);
  border-bottom: 1px solid var(--ds-amber-400);
}

/* shared/_stat_strip. column-reverse puts the value on top while keeping <dt> before
   <dd> in the DOM. Hairlines are a 1px gap over a tinted background, and flex rather than
   grid so a short last row grows to fill instead of leaving tinted empty cells. */
.stat-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 1px;
  margin: 0;
  background-color: var(--ds-gray-alpha-400);
  border: 1px solid var(--ds-gray-alpha-400);
  border-radius: var(--ds-radius);
  overflow: hidden;
}

.stat-strip__item {
  display: flex;
  flex: 1 1 8.5rem;
  flex-direction: column-reverse;
  gap: 0.125rem;
  padding: 0.625rem 0.875rem;
  background-color: var(--bs-body-bg);
}

/* Equal columns: two by two on a narrow screen, then one row of however many tiles there
   are from md up. Never three and one, and no empty cells when a tile is left out. */
.stat-strip--grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* The item is `column-reverse`, so flex-end is the top: numbers line up across a row even
   when one label wraps. */
.stat-strip--grid .stat-strip__item {
  justify-content: flex-end;
}

@media (min-width: 768px) {
  .stat-strip--grid {
    grid-template-columns: none;
    grid-auto-columns: minmax(0, 1fr);
    grid-auto-flow: column;
  }
}

.stat-strip__value {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.3;
}

/* shared/_fact_list. `--framed` is the standalone variant, held between two hairlines. */
.fact-list {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.375rem 1.25rem;
  margin: 0;
  font-size: 0.875rem;
}

.fact-list--framed {
  padding: 0.625rem 0;
  border-top: 1px solid var(--ds-gray-alpha-400);
  border-bottom: 1px solid var(--ds-gray-alpha-400);
}

.fact-list__item {
  display: flex;
  align-items: baseline;
  gap: 0.375rem;
}

.fact-list__value {
  margin: 0;
}

.fact-list__value .badge + .badge {
  margin-left: 0.25rem;
}

/* A badge is inline-block, which the link's underline does not reach; without this a
   linked badge has no affordance. */
.fact-list__value a:hover > .badge,
.fact-list__value a:focus-visible > .badge {
  text-decoration: underline;
}

/* Small caps in the secondary colour, so a label reads as an index to its value. */
.stat-strip__label,
.fact-list__label {
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ds-gray-900);
}

/* Loading state is driven by Turbo's [aria-busy] on <turbo-frame>, not JS state, so it
   cannot get stuck on. */
turbo-frame[aria-busy] .list-group {
  opacity: 0.5;
}

[data-issues-loader] {
  display: none;
}

turbo-frame[aria-busy] [data-issues-loader] {
  display: inline-block;
}

/* /advanced-issues' slow-count note: the same [aria-busy] switch, held back three
   seconds by an animation delay so a fast answer never flashes it. */
[data-slow-loading-note] {
  display: none;
}

turbo-frame[aria-busy] [data-slow-loading-note] {
  display: block;
  opacity: 0;
  animation: slow-loading-note-appear 0.3s ease-in 3s forwards;
}

@keyframes slow-loading-note-appear {
  to { opacity: 1; }
}

/* ui/multi_select: the domain facet has forty-odd values, so the menu must scroll
   rather than run off the viewport. */
.multi-select-menu {
  max-height: 16rem;
  min-width: 15rem;
  overflow-y: auto;
}

/* <iconify-icon> renders asynchronously; sizing the empty box to 1em keeps the icon
   from shifting layout when it arrives (docs/ui.md §6). */
iconify-icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
}

/* Rendered issue text (docs/markdown.md): wide tables and long URLs must not push the
   page sideways, and `pre` scrolls rather than wraps. Colours are theme tokens only. */
.markdown-body {
  overflow-wrap: anywhere;
}

.markdown-body > :first-child {
  margin-top: 0;
}

.markdown-body > :last-child {
  margin-bottom: 0;
}

.markdown-body pre {
  padding: 0.75rem 1rem;
  border-radius: var(--ds-radius);
  background-color: var(--ds-gray-100);
  overflow-x: auto;
}

.markdown-body code {
  padding: 0.125rem 0.25rem;
  border-radius: var(--ds-radius);
  background-color: var(--ds-gray-100);
}

/* Inside a fence the box has the tint and the padding already; a second layer reads as a
   rendering bug. */
.markdown-body pre code {
  padding: 0;
  background-color: transparent;
}

.markdown-body table {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
}

.markdown-body th,
.markdown-body td {
  padding: 0.375rem 0.75rem;
  border: 1px solid var(--bs-border-color);
}

.markdown-body th {
  font-weight: 500;
}

.markdown-body img {
  max-width: 100%;
  height: auto;
  border-radius: var(--ds-radius);
}

.markdown-body blockquote {
  margin: 0 0 1rem;
  padding-left: 1rem;
  border-left: 2px solid var(--ds-gray-300);
  color: var(--ds-gray-900);
}

/* GFM's task list: no bullet (the checkbox is the marker), and the box is pulled back
   into the space the bullet would have used so it lines up with the text above it. */
.markdown-body li:has(> input[type="checkbox"]) {
  list-style: none;
}

.markdown-body li > input[type="checkbox"] {
  margin: 0 0.375rem 0 -1.25rem;
}

/* Blog headings are the page's own sections; the scrubber drops classes, so their
   scale is set here. */
.blog-post h2 {
  margin-top: 2.5rem;
  font-size: calc(1.275rem + 0.3vw);
  font-weight: 600;
}

.blog-post h3 {
  margin-top: 1.5rem;
  font-size: 1.25rem;
  font-weight: 600;
}

@media (min-width: 1200px) {
  .blog-post h2 {
    font-size: 1.5rem;
  }
}

/* Near By Guru (docs/near-by-guru.md §6.4–§6.5.1). leaflet.css loads before Bootstrap
   (the page's content_for :head), so these overrides win on load order with the library's
   own selectors. Colours are theme tokens only. */
.nearby-view-toggle {
  display: inline-flex;
  gap: 0.25rem;
  margin-bottom: 1rem;
  padding: 0.25rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ds-radius);
  background-color: var(--ds-background-100);
}

.nearby-view-tab {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.25rem 0.75rem;
  border: 0;
  border-radius: calc(var(--ds-radius) - 2px);
  background: transparent;
  color: var(--ds-gray-900);
  font-size: 0.875rem;
  font-weight: 500;
}

.nearby-view-tab:hover {
  color: var(--ds-gray-1000);
}

.nearby-view-tab.active {
  background-color: var(--ds-gray-1000);
  color: var(--ds-background-100);
}

.nearby-view-tab:focus-visible {
  outline: 0;
  box-shadow: var(--ds-focus-border);
}

.nearby-map {
  position: relative;
}

/* Tall enough for a 48-person ring at the smallest avatar size on a phone (§6.5.1). */
.nearby-map-canvas {
  height: clamp(320px, 60vh, 520px);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ds-radius);
  overflow: hidden;
}

.nearby-map-fallback {
  padding: 2rem 1rem;
  border: 1px dashed var(--bs-border-color);
  border-radius: var(--ds-radius);
  text-align: center;
}

.nearby-map-note {
  margin-top: 0.5rem;
}

/* Above Leaflet's panes (z-index 400) and beside its controls (1000), top right where no
   control sits. */
.nearby-map-status {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 1000;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ds-radius);
  background-color: var(--ds-background-100);
  box-shadow: var(--bs-box-shadow-sm);
}

.nearby-map-status-link {
  display: inline-flex;
  padding: 0.125rem;
  color: var(--ds-gray-1000);
}

.leaflet-container {
  background: var(--ds-background-200);
  font-family: var(--bs-body-font-family);
}

.leaflet-container a {
  color: var(--ds-gray-1000);
}

.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
  background: var(--ds-background-100);
  color: var(--bs-body-color);
  box-shadow: var(--bs-box-shadow);
}

.leaflet-container .leaflet-control-attribution {
  background: rgba(var(--bs-body-bg-rgb), 0.8);
  color: var(--ds-gray-900);
}

.leaflet-bar a {
  background-color: var(--ds-background-100);
  border-bottom-color: var(--bs-border-color);
  color: var(--ds-gray-1000);
}

/* Without MAP_TILE_URL_DARK the light tiles are inverted rather than left glaring. */
[data-bs-theme="dark"] .leaflet-tile {
  filter: invert(1) hue-rotate(180deg) brightness(0.9) contrast(0.9);
}

/* One L.divIcon per city holds its whole ring (docs/near-by-guru.md §6.5.1): the avatars sit
   at fixed pixel offsets inside it, so the ring keeps its size through a zoom. Leaflet places
   the wrapper with an inline `transform: translate3d(x, y)`; only the avatars take pointer
   events, so a click between two of them still reaches the map. */
.nearby-cluster {
  pointer-events: none;
}

.nearby-cluster-ring {
  position: relative;
}

/* "+N": the people this zoom has no room for (§6.5.1). Centred on its point on the ring's edge,
   in the inverse colours so it reads against both the tiles and the avatars. */
.nearby-cluster-more {
  position: absolute;
  transform: translate(-50%, -50%);
  z-index: 4;
  padding: 0.125rem 0.5rem;
  border: 2px solid var(--ds-background-100);
  border-radius: 999px;
  background-color: var(--ds-gray-1000);
  color: var(--ds-background-100);
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.4;
  white-space: nowrap;
  pointer-events: auto;
  cursor: pointer;
  box-shadow: var(--bs-box-shadow-sm);
}

.nearby-cluster-more:hover,
.nearby-cluster-more:focus-visible {
  outline: 0;
  box-shadow: var(--ds-focus-border);
}

/* Nothing here may scale the avatar: the individual `scale` property is applied before a
   transform and scales the translation too, which once threw an avatar ~100px away from the
   pointer on hover and click. The lift scales the image inside instead. */
.nearby-avatar {
  position: absolute;
  padding: 0;
  border: 0;
  pointer-events: auto;
  cursor: pointer;
  border-radius: 50%;
  background: var(--ds-background-100);
  box-shadow: 0 0 0 2px var(--ds-background-100), var(--bs-box-shadow-sm);
}

.nearby-avatar img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  transition: transform 0.12s ease-out;
}

.nearby-avatar:hover,
.nearby-avatar:focus-visible {
  z-index: 2;
  outline: 0;
  box-shadow: 0 0 0 2px var(--ds-background-100), var(--ds-focus-border);
}

.nearby-avatar:hover img,
.nearby-avatar:focus-visible img {
  transform: scale(1.1);
}

/* The avatar whose popup is open: ringed and on top, so it is not lost among the others. */
.nearby-avatar.nearby-avatar-selected {
  z-index: 3;
  box-shadow: 0 0 0 2px var(--ds-background-100), 0 0 0 5px var(--bs-primary);
}

.nearby-avatar.nearby-avatar-selected img {
  transform: scale(1.1);
}

/* A city's count at wider zooms (§6.5.2): a link to its page. */
.nearby-bubble-icon {
  pointer-events: none;
}

.nearby-bubble {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background-color: rgba(var(--bs-primary-rgb), 0.85);
  box-shadow: 0 0 0 2px var(--ds-background-100), var(--bs-box-shadow-sm);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  pointer-events: auto;
}

.nearby-bubble:hover,
.nearby-bubble:focus-visible {
  outline: 0;
  background-color: var(--bs-primary);
  box-shadow: 0 0 0 2px var(--ds-background-100), var(--ds-focus-border);
  color: #fff;
}

/* Developers in view, bottom left above the tiles, clear of the attribution on the right. */
.nearby-map-summary {
  position: absolute;
  bottom: 0.75rem;
  left: 0.75rem;
  z-index: 1000;
  max-width: calc(100% - 1.5rem);
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ds-radius);
  background-color: var(--ds-background-100);
  box-shadow: var(--bs-box-shadow-sm);
  pointer-events: none;
}

.nearby-map-popup {
  display: grid;
  gap: 0.25rem;
  min-width: 11rem;
}

.nearby-map-popup-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.nearby-map-popup-header img {
  border-radius: 50%;
}

/* Why Near By Guru (app/views/nearby/show.html.erb): a quiet box under the intro. */
.nearby-why {
  background-color: var(--bs-tertiary-bg);
}

.nearby-why-list {
  display: grid;
  gap: 0.75rem;
  padding-inline-start: 1.25rem;
}

.nearby-why-list li::marker {
  font-weight: 600;
}

/* The picked person's card, docked in the map's top-right corner (bottom edge on a phone).
   Fixed in place, so picking an avatar never pans the map under the pointer. Above Leaflet's
   panes (400–700) and controls (800–1000). */
.nearby-map-stage {
  position: relative;
}

.nearby-map-card:not([hidden]) {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 1100;
  width: min(22rem, calc(100% - 1.5rem));
  max-height: calc(100% - 1.5rem);
  overflow-y: auto;
  padding: 0.75rem 0.875rem;
  font-size: 0.875rem;
  line-height: 1.4;
  background: var(--ds-background-100);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--ds-radius);
  box-shadow: var(--bs-box-shadow);
}

.nearby-map-card-close {
  float: right;
  margin: 0 0 0.25rem 0.5rem;
}

@media (max-width: 575.98px) {
  .nearby-map-card:not([hidden]) {
    top: auto;
    bottom: 0.75rem;
    left: 0.75rem;
    width: auto;
    max-height: 60%;
  }
}

.nearby-map-popup-card .nearby-person {
  padding: 0;
  border: 0;
}

/* The card picked on the map: lifted to the top of the list and highlighted until the
   popup closes (nearby-view#selectPerson). */
.nearby-person.nearby-person-selected {
  margin-inline: -0.75rem;
  padding-inline: 0.75rem;
  border-radius: var(--bs-border-radius);
  background-color: var(--bs-primary-bg-subtle);
  box-shadow: inset 3px 0 0 var(--bs-primary);
  transition: background-color 0.2s ease-out;
}

/* Person card (app/views/nearby/_person.html.erb). The avatar box is fixed so a slow
   image never shifts the list, and it has a tint while it loads, in both themes. */
.nearby-person-avatar {
  width: 48px;
  height: 48px;
  object-fit: cover;
  background-color: var(--ds-gray-100);
}

/* A login or repository name has no spaces to break at; on a phone it wraps or truncates
   instead of pushing the card wider than the screen. */
.nearby-person-body {
  min-width: 0;
}

.nearby-person-name,
.nearby-person-login,
.nearby-person-company,
.nearby-person-bio {
  overflow-wrap: anywhere;
}

.nearby-person-repositories li + li {
  margin-top: 0.25rem;
}

.nearby-person-repositories a {
  max-width: 100%;
  min-width: 0;
}

.nearby-person-repositories span {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-variant-numeric: tabular-nums;
}

.nearby-person-repositories .badge {
  --bs-badge-font-size: 0.6875rem;
  --bs-badge-padding-y: 0.125rem;
}

.nearby-person-place,
.nearby-person-location {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
}

.nearby-person-location span {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* The signed-in lists in columns (nearby_personal/show): every cell in a row is as tall as
   the tallest, so the cards' bottom rules line up across the row. */
.nearby-people-grid > .col {
  display: flex;
  flex-direction: column;
}

.nearby-people-grid > .col > .nearby-person {
  flex-grow: 1;
}
