/* Force the [hidden] attribute to win against inline display:flex /
   display:grid declarations. Several panels / banners / toasts ship
   with inline `display: flex` and toggle visibility via `el.hidden =
   true`, which silently fails because inline `display` beats the UA's
   `[hidden] { display: none }`. !important here means setting `hidden`
   actually hides the element regardless of its inline display value. */
[hidden] { display: none !important; }

/* The MapLibre-provided GeolocateControl is added to the map (so
   .trigger() works + the accuracy circle layer registers) but its
   default top-right button is hidden — we expose location via the
   custom #locate-me button in the right-rail instead, so it sits
   alongside zoom / layer / share for visual consistency. */
.maplibregl-ctrl-top-right .maplibregl-ctrl-geolocate,
.maplibregl-ctrl-top-right .maplibregl-ctrl-group:has(.maplibregl-ctrl-geolocate) {
  display: none !important;
}

/* In dark mode the white rail buttons sit on the dark map base with
   no visual grouping — they read as floating dots. Wrap them in a
   subtle translucent gray pill so the column reads as a single
   control surface. Light mode keeps the existing free-standing look. */
html[data-theme="dark"] #right-rail {
  background: rgba(36, 28, 18, 0.55);
  backdrop-filter: var(--blur-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  padding: 8px 6px;
  border-radius: 26px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: var(--shadow-2);
}

/* ─── Haive desktop map — design overrides ─────────────────────────────
 * Extracted from the inline <style> block previously emitted via JS in
 * app/views/app/map/index.html.erb. Tokens come from the layout
 * (:root + [data-theme=…]); rules here only reference var(--…).
 *
 * Browser caches this file separately from the HTML so cold-load
 * payload drops by ~20 KB and a return visit hits the disk cache.
 * ─────────────────────────────────────────────────────────────────── */

/* Side panel + opposite-rail positioning. When data-panel-side is
   "right" the panel jumps to the right edge and the right-rail
   (zoom + layer + filter buttons) moves to the left edge. */
#map-area[data-panel-side='left']  #side-panel { left: 10px;  right: auto; }
#map-area[data-panel-side='left']  #right-rail { right: 16px; left: auto; }
#map-area[data-panel-side='right'] #side-panel { right: 10px; left: auto; }
#map-area[data-panel-side='right'] #right-rail { left: 16px;  right: auto; }
/* Resize handle docks on the panel's inner edge (toward the map). */
#map-area[data-panel-side='left']  #side-panel-resize { right: 0; }
#map-area[data-panel-side='right'] #side-panel-resize { left:  0; }
#side-panel-resize:hover { background: var(--brand-yellow); opacity: 0.4; }
/* Mirror the side-panel header when the panel is on the right. */
#map-area[data-panel-side='right'] #side-panel-header { flex-direction: row-reverse; }
/* Open / closed state — slide the panel off-screen on close. */
#map-area[data-panel-open='true']  #side-panel { transform: translateX(0); pointer-events: auto; }
#map-area[data-panel-open='false'][data-panel-side='left']  #side-panel { transform: translateX(calc(-100% - 20px)); pointer-events: none; }
#map-area[data-panel-open='false'][data-panel-side='right'] #side-panel { transform: translateX(calc(100% + 20px));  pointer-events: none; }
/* Tab visibility driven by data-tab-visible. */
#map-area #side-panel-tab { display: none; }
#map-area[data-tab-visible='true'] #side-panel-tab { display: flex; }
#map-area[data-panel-side='left']  #side-panel-tab { left: 0;  border-radius: 0 12px 12px 0; flex-direction: column; }
#map-area[data-panel-side='right'] #side-panel-tab { right: 0; border-radius: 12px 0 0 12px; flex-direction: column; }
/* Profile / buzz detail panels share the same edge as the around-you
   panel. The [hidden] !important is load-bearing — inline display:flex
   would otherwise beat the UA [hidden]{display:none}. */
#map-area[data-panel-side='left']  #profile-detail { left: 10px;  right: auto; }
#map-area[data-panel-side='right'] #profile-detail { right: 10px; left: auto; }
#profile-detail[hidden] { display: none !important; }
#map-area[data-panel-side='left']  #buzz-detail { left: 10px;  right: auto; }
#map-area[data-panel-side='right'] #buzz-detail { right: 10px; left: auto; }
#buzz-detail[hidden] { display: none !important; }
/* Top-to-bottom label inside the side-panel-tab. */
#side-panel-tab-label { transform: rotate(0deg); }
/* While bootstrapping (data-panel-init=true), kill transitions so
   the panel snaps to its persisted state without animating from the
   default-open position. JS removes the attribute after first paint. */
#map-area[data-panel-init='true'] #side-panel,
#map-area[data-panel-init='true'] #side-panel-tab { transition: none !important; }

/* ─── Design system overrides ─────────────────────────────────────────
   Glass surfaces, dark-mode-aware colours, spring motion, marker
   hover / active states, skeleton shimmer, polish. */

#side-panel, #profile-detail, #buzz-detail, #ai-results,
#layer-menu, #marker-filter-menu, #ai-history-menu, #address-suggestions {
  background: var(--color-surface) !important;
  backdrop-filter: var(--blur-glass);
  -webkit-backdrop-filter: var(--blur-glass);
  color: var(--color-text) !important;
  border: 1px solid var(--color-border-soft);
}
/* #address-bar is just a flex wrapper around the picker pill now;
   the visible chrome lives on #address-pill inside. Keep the wrapper
   transparent so its parent row doesn't pick up a glass card border. */
#address-bar { background: transparent !important; border: 0 !important; backdrop-filter: none; -webkit-backdrop-filter: none; }
#side-panel, #profile-detail, #buzz-detail {
  transition: transform var(--dur-panel) var(--ease-spring), opacity var(--dur-base) var(--ease-spring);
}
/* Layer menu / filter menu / history menu items respect the theme. */
#layer-menu button, #marker-filter-menu .marker-filter-row, #ai-history-menu button {
  color: var(--color-text);
  background: transparent !important;
}
#layer-menu button:hover, #marker-filter-menu .marker-filter-row:hover, #ai-history-menu button:hover {
  background: var(--color-border-soft) !important;
}
/* Right-rail buttons + zoom + theme toggle pick up theme colours. */
#right-rail button, #side-panel-tab, #side-panel-flip, #side-panel-close,
#profile-detail-back, #buzz-detail-back, #anchor-toggle {
  background: var(--color-surface) !important;
  color: var(--color-text) !important;
  backdrop-filter: var(--blur-glass);
  border: 1px solid var(--color-border-soft);
  transition: transform var(--dur-quick) var(--ease-spring), background var(--dur-quick) var(--ease-soft);
}
#right-rail button:hover, #side-panel-flip:hover, #side-panel-close:hover,
#profile-detail-back:hover, #buzz-detail-back:hover {
  transform: scale(1.06);
}
#right-rail button:active { transform: scale(0.96); }
#map-area { background: var(--color-bg); }
#ai-input, #address-input, #ai-input::placeholder, #address-input::placeholder {
  color: var(--color-text);
}
#ai-input::placeholder, #address-input::placeholder { color: var(--color-text-muted); }
/* Side-panel rows. */
#side-panel-list button, #ai-results-list button {
  transition: transform var(--dur-quick) var(--ease-spring),
              box-shadow var(--dur-quick) var(--ease-soft);
}
#side-panel-list button:hover, #ai-results-list button:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-2);
}

/* MapLibre marker hover / active. The marker root keeps MapLibre's
   `transform: translate(x, y)`; hover + active scale targets the inner
   .haive-marker-inner instead, so we don't fight MapLibre's positioning. */
.maplibregl-marker .haive-marker-inner {
  transition: transform var(--dur-quick) var(--ease-spring);
  transform-origin: center;
  will-change: transform;
  position: relative;
}
/* Markers live on the map plane — their z-index stays in single digits
   so a hovered / active / spidered marker can never poke over the
   floating UI (side panel z:8, top-block z:9, header z:10, …). It only
   needs to clear the radius-circle overlay (z:4). */
.maplibregl-marker:hover { z-index: 5 !important; }
.maplibregl-marker:hover .haive-marker-inner { transform: scale(1.12); }
.maplibregl-marker.haive-marker-spidered { z-index: 6 !important; }
.maplibregl-marker.haive-marker-active { z-index: 7 !important; }
.maplibregl-marker.haive-marker-active .haive-marker-inner { transform: scale(1.18); }
.maplibregl-marker.haive-marker-active .haive-marker-inner > div:first-child {
  box-shadow: 0 0 0 3px var(--brand-yellow), 0 4px 14px rgba(0,0,0,0.35);
}
@keyframes haive-marker-pulse {
  0% { transform: scale(0.8); opacity: 0.6; }
  80%, 100% { transform: scale(1.9); opacity: 0; }
}
@keyframes haive-marker-arrive {
  0% { transform: scale(0.6); opacity: 0; box-shadow: 0 0 0 8px rgba(246,179,0,0.55); }
  70% { transform: scale(1.1); opacity: 1; box-shadow: 0 0 0 0 rgba(246,179,0,0); }
  100% { transform: scale(1); opacity: 1; box-shadow: 0 0 0 0 rgba(246,179,0,0); }
}
.haive-marker-arrive .haive-marker-inner > div {
  animation: haive-marker-arrive 700ms var(--ease-spring) both;
}

/* ── Density-dot mode ────────────────────────────────────────────────
   Between the cluster scale and the full-marker scale (≈ z14–16) every
   individual marker collapses to a small solid-colored dot: each marker
   carries TWO children — the dot element (first child, `display:none`
   by default) and the full marker (second child, the hexagon / bee).
   In dot mode we flip the dot on and hide the full one, mirroring the
   density-dot tier the iOS + Android maps use (separate dot views), so a
   busy neighbourhood reads as a manageable sprinkle of dots instead of a
   wall of 30px markers. `!important` because the per-state appearance is
   inline on the elements. */
.maplibregl-marker.haive-marker-dot .haive-marker-inner > div:first-child {
  display: block !important;
  width: 11px !important;
  height: 11px !important;
  border-radius: 50% !important;
  border-width: 1.5px !important;
  box-shadow: 0 1px 3px rgba(0,0,0,0.3) !important;
}
.maplibregl-marker.haive-marker-dot .haive-marker-inner > div:first-child > * { display: none !important; }
.maplibregl-marker.haive-marker-dot .haive-marker-inner > *:not(:first-child) { display: none !important; }
.maplibregl-marker.haive-marker-dot:hover .haive-marker-inner { transform: scale(1.6); }
/* No pulse ring / arrive bloom on a dot — it'd dwarf the dot itself. */
.maplibregl-marker.haive-marker-dot.haive-marker-active .haive-marker-inner::before { display: none !important; }
.maplibregl-marker.haive-marker-dot .haive-marker-inner > div:first-child {
  animation: none !important;
}
.maplibregl-marker.haive-marker-active .haive-marker-inner::before {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  background: var(--brand-yellow);
  animation: haive-marker-pulse 1.6s var(--ease-soft) infinite;
  z-index: -1;
  pointer-events: none;
}

/* Skeleton shimmer used while AI is thinking and while lazy
   about-fetches are in flight. */
@keyframes haive-shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}
.haive-skeleton {
  background: linear-gradient(90deg,
    var(--color-border-soft) 0%, var(--color-border) 50%, var(--color-border-soft) 100%);
  background-size: 200% 100%;
  animation: haive-shimmer 1.4s var(--ease-soft) infinite;
  border-radius: var(--r-sm);
}
/* AI results panel slide-up entrance. */
@keyframes haive-rise {
  from { transform: translateY(8px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}
#ai-results:not([hidden]) { animation: haive-rise var(--dur-base) var(--ease-spring); }
#profile-detail:not([hidden]), #buzz-detail:not([hidden]) {
  animation: haive-rise var(--dur-panel) var(--ease-spring);
}
/* Snackbar — replaces #toast styling with bottom-left + brand pill. */
#toast {
  background: var(--color-surface) !important;
  color: var(--color-text) !important;
  border: 1px solid var(--color-border-soft);
  backdrop-filter: var(--blur-glass);
}

/* Side panel header / tabs / chrome buttons pick up tokens. */
#side-panel-header { border-bottom-color: var(--color-border-soft) !important; }
#side-panel-tabs { border-bottom-color: var(--color-border-soft) !important; }
#side-panel-count { color: var(--color-text-muted) !important; }
#side-panel-flip, #side-panel-close { background: var(--color-border-soft) !important; color: var(--color-text) !important; }
#profile-detail header, #buzz-detail header { border-bottom-color: var(--color-border-soft) !important; }
#profile-detail header { color: var(--color-text); }
#profile-detail-title, #buzz-detail-title { color: var(--color-text); }
#profile-detail-back, #buzz-detail-back { background: var(--color-border-soft) !important; color: var(--color-text) !important; }
.side-panel-tab, .profile-detail-tab {
  color: var(--color-text-soft) !important;
  transition: color var(--dur-quick) var(--ease-soft);
}
.side-panel-tab[aria-selected='true'], .profile-detail-tab[aria-selected='true'] {
  color: var(--color-text) !important;
}
/* Active-tab underline (replaces the cream-tint bg). Uses ::after so
   we don't reflow the button's box. */
.side-panel-tab, .profile-detail-tab {
  position: relative;
  background: transparent !important;
}
.side-panel-tab::after, .profile-detail-tab::after {
  content: '';
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 0;
  height: 2px;
  background: var(--brand-yellow);
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: 50% 50%;
  transition: transform var(--dur-base) var(--ease-spring);
}
.side-panel-tab[aria-selected='true']::after,
.profile-detail-tab[aria-selected='true']::after {
  transform: scaleX(1);
}
.side-panel-tab-count, #profile-detail-buzz-count { color: var(--color-text-muted) !important; }

/* AI bar / address bar / inputs / placeholder all theme-aware. */
#ai-input, #address-input { color: var(--color-text); }
#ai-input::placeholder, #address-input::placeholder { color: var(--color-text-muted); }
#ai-input:disabled { opacity: 0.55; }
#ai-search-icon { color: var(--brand-yellow); }
#ai-clear, #ai-history-toggle { color: var(--color-text-soft) !important; }
#address-bar, #address-suggestions { color: var(--color-text); }
#ai-results-answer {
  color: var(--color-text);
  word-wrap: break-word;
  overflow-wrap: break-word;
}
#ai-results-tabs { border-bottom-color: var(--color-border-soft) !important; }

/* Right-rail buttons + theme toggle text colour follows tokens. */
#right-rail button { color: var(--color-text) !important; }
#right-rail button:disabled { opacity: 0.5; cursor: not-allowed; }

/* Marker-filter & layer-menu items follow tokens. */
#marker-filter-menu .marker-filter-row { color: var(--color-text); }
#layer-menu button { border-bottom: 1px solid var(--color-border-soft) !important; }
#radius-label { color: var(--color-text-soft) !important; }

/* :focus-visible — visible outline for keyboard users. */
button:focus-visible, a:focus-visible, input:focus-visible, [role='tab']:focus-visible,
.side-panel-tab:focus-visible, .profile-detail-tab:focus-visible {
  outline: 2px solid var(--brand-yellow) !important;
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
/* AI question + address inputs sit inside their own bordered pills, so
   the global yellow focus ring doubles up with the pill chrome. Drop
   the ring on these two inputs specifically. */
#ai-input:focus, #ai-input:focus-visible,
#address-input:focus, #address-input:focus-visible {
  outline: none !important;
  box-shadow: none !important;
}

/* Wider laptops (≤1440 px, but still > 1024 px where the panel
   becomes a bottom sheet) — the side panel is still docked on the
   side, and the centred search bar's clamp(346, 65vw, 778) starts
   eating into it. Shrink the search bar so its left edge clears
   the panel's right edge by ≥20 px of gutter.

   width = 100vw − 2 × (panelWidth + 10 px dock + 20 px gutter)
           − 20 px additional trim.
   max() with the original 346 px lower bound prevents the calc
   from going absurdly small if this rule ever leaks below 1024 px. */
@media (min-width: 1025px) and (max-width: 1440px) {
  #top-block {
    width: max(346px, calc(100vw - 2 * clamp(280px, 22vw, 360px) - 80px)) !important;
  }
}

/* Narrow desktops / tablets (≤1024 px) — at this width the centred
   search bar overlaps the side panel docked on the side. Reshape
   the side panel into a bottom sheet instead so the search bar
   keeps its centred top slot. The right-rail (nav icons) is
   locked to the right edge and the flip-sides button is hidden
   (its swap-x semantic doesn't apply once the panel is on the
   bottom). The profile + buzz detail panels share the same edge,
   so they get the same bottom-sheet treatment. */
@media (max-width: 1024px) {
  #map-area #side-panel,
  #map-area #profile-detail,
  #map-area #buzz-detail {
    top: auto !important;
    bottom: 10px !important;
    left: 10px !important;
    right: 10px !important;
    width: auto !important;
    height: 45vh !important;
    max-height: 60vh !important;
  }
  /* Closed-state slide-out becomes a slide-down. */
  #map-area[data-panel-open='false'] #side-panel {
    transform: translateY(calc(100% + 20px)) !important;
  }
  /* Reopen tab anchors to the bottom edge as a horizontal pill. */
  #map-area #side-panel-tab {
    top: auto !important;
    bottom: 0 !important;
    left: 50% !important;
    right: auto !important;
    transform: translateX(-50%) !important;
    width: 200px !important;
    height: 32px !important;
    border-radius: 12px 12px 0 0 !important;
    flex-direction: row !important;
    padding: 0 12px !important;
  }
  #side-panel-tab-label {
    writing-mode: horizontal-tb !important;
    text-orientation: mixed !important;
  }
  /* Resize handle moves to the top edge of the now-horizontal panel
     and becomes a vertical-drag affordance. */
  #map-area #side-panel-resize {
    top: 0 !important;
    bottom: auto !important;
    left: 0 !important;
    right: 0 !important;
    width: auto !important;
    height: 6px !important;
    cursor: row-resize !important;
  }
  #map-area #side-panel-header { flex-direction: row !important; }

  /* Nav-icon column always on the right at this breakpoint, even
     if the persisted data-panel-side is "right" (which used to
     send the rail to the left to make room for the side panel). */
  #map-area #right-rail { right: 16px !important; left: auto !important; }

  /* Flip-sides button — its swap-x semantic doesn't apply to a
     bottom sheet, so hide it. */
  #side-panel-flip { display: none !important; }
}

/* Mobile responsive — clamp panel widths + drop min-width below 480 px
   so floating chrome doesn't overshoot the viewport. */
@media (max-width: 480px) {
  #side-panel { width: min(380px, 92vw) !important; }
  #profile-detail, #buzz-detail { width: min(360px, 92vw) !important; }
  #address-search-row { width: 92vw !important; min-width: 0 !important; bottom: 90px !important; }
  #ai-results { max-height: 50vh !important; }
  header { padding-left: 12px !important; padding-right: 12px !important; }
  #side-panel-flip { display: none !important; }
  #right-rail button { width: 36px !important; height: 36px !important; }
}

/* 769–1024 px — side panel is already a bottom sheet at this
   breakpoint, so the search container can grow without overlapping.
   Bump width to 1.2× the base clamp so the inline rotator's 4
   prompt chips fit on a single row instead of wrapping. */
@media (min-width: 769px) and (max-width: 1024px) {
  #top-block {
    width: clamp(415px, 78vw, 934px) !important;
  }
}

/* ≤768 px — let the container take the full viewport minus a
   10 px gutter on each side, matching the bottom-sheet side
   panel's margins so both surfaces share the same edge. */
@media (max-width: 768px) {
  #top-block {
    width: calc(100vw - 20px) !important;
    min-width: 0 !important;
  }
}

/* Phones (≤425 px) — stack the address / radius row vertically so
   each pill gets the full width of the search card. The ≤768 px
   rule above already takes the search container to full width
   minus the 10 px side gutter, so no width override is needed
   here. */
@media (max-width: 425px) {
  #ai-address-radius-row {
    flex-direction: column !important;
    align-items: stretch !important;
  }
  #ai-address-radius-row #slider-pill {
    margin-left: 0 !important;
  }
  #ai-address-radius-row #radius-slider { width: 100% !important; }
}
@media (max-width: 360px) {
  #side-panel { width: 96vw !important; }
  #profile-detail, #buzz-detail { width: 96vw !important; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Map-loading ribbon at the top of the side panel — wired by JS when
   at least one in-flight fetch is running. */
#side-panel-loading-ribbon {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--brand-yellow), transparent);
  background-size: 200% 100%;
  animation: haive-shimmer 1.2s linear infinite;
  z-index: 2;
  pointer-events: none;
}
/* Make the side-panel header + tabs sticky so they don't scroll away
   when the user scrolls deep into the list. */
#side-panel-header {
  position: sticky;
  top: 0;
  background: var(--color-surface);
  backdrop-filter: var(--blur-glass);
  z-index: 3;
}
#side-panel-tabs {
  position: sticky;
  top: 49px;
  background: var(--color-surface);
  backdrop-filter: var(--blur-glass);
  z-index: 2;
}
/* Stronger blur on the AI search bar — focal element gets more depth. */
#top-block > div:first-child {
  backdrop-filter: blur(40px) saturate(180%);
  -webkit-backdrop-filter: blur(40px) saturate(180%);
  background: var(--color-surface) !important;
  color: var(--color-text);
}
/* Dropdown enter animation — opacity + 4 px rise. */
@keyframes haive-dropdown {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}
#layer-menu:not([hidden]),
#marker-filter-menu:not([hidden]),
#ai-history-menu:not([hidden]),
#address-suggestions:not([hidden]) {
  animation: haive-dropdown var(--dur-quick) var(--ease-spring);
}
/* Skip link — invisible until focused, then jumps over the header to
   the side panel for keyboard / screen-reader users. */
.skip-link {
  position: absolute;
  top: -40px;
  left: 8px;
  z-index: 100;
  padding: 8px 14px;
  background: var(--brand-yellow);
  color: var(--brand-brown);
  border-radius: var(--r-sm);
  font-weight: 700;
  text-decoration: none;
  transition: top var(--dur-quick) var(--ease-spring);
}
.skip-link:focus { top: 8px; }
