<template>
  <div class="shopfloor-map">
    <!-- Controls for legacy machine mode only (MapView handles filters for asset mode) -->
    <div class="map-controls" v-if="!pickerMode && !assetTypeMode">
      <div class="filters">
        <select v-model="filters.machinetype" @change="applyFilters">
          <option value="">All Types</option>
          <option v-for="t in machinetypes" :key="t.machinetypeid" :value="t.machinetypeid">
            {{ t.machinetype }}
          </option>
        </select>

        <select v-model="filters.businessunit" @change="applyFilters">
          <option value="">All Business Units</option>
          <option v-for="bu in businessunits" :key="bu.businessunitid" :value="bu.businessunitid">
            {{ bu.businessunit }}
          </option>
        </select>

        <select v-model="filters.status" @change="applyFilters">
          <option value="">All Statuses</option>
          <option v-for="s in statuses" :key="s.statusid" :value="s.statusid">
            {{ s.status }}
          </option>
        </select>

        <input
          type="text"
          v-model="filters.search"
          placeholder="Search..."
          @input="debounceSearch"
        />
      </div>

      <div class="legend">
        <span
          v-for="t in visibleTypes"
          :key="t.machinetypeid"
          class="legend-item"
        >
          <span class="legend-dot" :style="legendDotStyle(getTypeColor(t.machinetype))"></span>
          {{ t.machinetype }}
        </span>
      </div>
    </div>

    <!-- Legend for asset type mode - shows subtypes when a type is selected -->
    <div class="map-legend" v-if="!pickerMode && assetTypeMode && hasLegendEntries">
      <!-- Show subtype legend when a specific type is selected -->
      <template v-if="selectedAssetType && Object.keys(visibleSubtypes).length">
        <span
          v-for="(color, subtypeId) in visibleSubtypes"
          :key="subtypeId"
          class="legend-item"
        >
          <span class="legend-dot" :style="legendDotStyle(color)"></span>
          {{ subtypeNames[subtypeId] || `Type ${subtypeId}` }}
        </span>
      </template>
      <!-- Show asset type legend when no type is selected -->
      <template v-else>
        <span
          v-for="(color, assetType) in visibleAssetTypes"
          :key="assetType"
          class="legend-item"
        >
          <span class="legend-dot" :style="legendDotStyle(color)"></span>
          {{ assetTypeLabels[assetType] || assetType }}
        </span>
      </template>
      <!-- Plugin-contributed overlay legend entries (ADR-010 get_map_overlays) -->
      <span
        v-for="(entry, i) in overlayLegend"
        :key="`overlay-${i}`"
        class="legend-item"
      >
        <span class="legend-dot legend-ring" :style="{ borderColor: entry.color }"></span>
        {{ entry.label }}
      </span>
    </div>

    <div class="picker-controls" v-if="pickerMode">
      <span class="picker-message">Click on the map to set location</span>
      <span v-if="pickedPosition" class="picker-coords">
        Position: {{ pickedPosition.left }}, {{ pickedPosition.top }}
      </span>
      <button class="btn btn-secondary btn-sm" @click="clearPosition">Clear</button>
    </div>

    <div ref="mapContainer" class="map-container" :class="{ 'picker-active': pickerMode }"></div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import { loadMapConfig, blueprintUrlFor, dimensionsFor, hasLevel, state as mapConfig } from '../composables/mapConfig'
import { assetTypeLabel } from '../utils/assetTypes'
import { getSubtypeId, markerRingColor, UNSPECIFIED_COLOR } from '../utils/mapColors'
import api from '../api'

const props = defineProps({
  machines: { type: Array, default: () => [] },
  machinetypes: { type: Array, default: () => [] },
  businessunits: { type: Array, default: () => [] },
  statuses: { type: Array, default: () => [] },
  theme: { type: String, default: 'dark' },
  pickerMode: { type: Boolean, default: false },
  initialPosition: { type: Object, default: null },  // { left, top }
  assetTypeMode: { type: Boolean, default: false },  // When true, use unified asset format
  selectedAssetType: { type: String, default: '' },  // Currently selected asset type filter
  subtypeColors: { type: Object, default: () => ({}) },  // Map of subtype ID to color
  subtypeNames: { type: Object, default: () => ({}) },  // Map of subtype ID to name
  // Which level this map is drawing (ADR-017). Defaults to the current level in
  // the shared config, so an existing caller that has not been taught about
  // levels still renders the level the user is looking at rather than nothing.
  levelid: { type: Number, default: null }
})

const emit = defineEmits(['markerClick', 'positionPicked'])

// An empty legend still drew its bar and border, which looked like a stray input
// box under the toolbar. It renders only when it has something in it.
const hasLegendEntries = computed(() => {
  if (props.selectedAssetType && Object.keys(visibleSubtypes.value).length) return true
  if (Object.keys(visibleAssetTypes.value).length) return true
  return overlayLegend.value.length > 0
})

const mapContainer = ref(null)
let map = null
let imageOverlay = null
const markers = ref([])
const pickedPosition = ref(null)
let pickerMarker = null
let markerLayer = null
let canvasRenderer = null

// ADR-010 map overlays (get_map_overlays): plugin-declared per-asset decoration
// (ring/badge) drawn on top of markers, plus legend entries. overlayLayers are
// the extra Leaflet layers, cleared and redrawn with the markers.
const overlayDecorations = ref([])  // [{ style, byAsset: Map(assetid -> {color,label}) }]
const overlayLegend = ref([])       // [{ label, color }] distinct entries
let overlayLayers = []

const filters = ref({
  machinetype: '',
  businessunit: '',
  status: '',
  search: ''
})

// The drawn level's native size, which is what its marker coordinates mean.
// Mutable because the levels load after this module does, and because switching
// level changes them.
function drawnLevelId() {
  return props.levelid ?? mapConfig.currentlevelid
}

let MAP_WIDTH = dimensionsFor(drawnLevelId()).width
let MAP_HEIGHT = dimensionsFor(drawnLevelId()).height

// Asset type colors (for unified map mode) - normalized lookup
const assetTypeColorsMap = {
  'machine': '#F44336',         // Red
  'computer': '#2196F3',        // Blue
  'printer': '#4CAF50',         // Green
  'network device': '#FF9800',  // Orange
  'network_device': '#FF9800',  // Orange (alternate key)
  'measuring_tool': '#9C27B0',  // Purple
  'measuring tool': '#9C27B0'   // Purple (normalized key)
}

// Get asset type color with case-insensitive lookup
function getAssetTypeColor(assettype) {
  if (!assettype) return '#BDBDBD'
  const normalized = assettype.toLowerCase()
  return assetTypeColorsMap[normalized] || '#BDBDBD'
}

// Asset-type display labels come from the shared util (single source of truth).
const assetTypeLabels = new Proxy({}, {
  get(target, prop) {
    return typeof prop === 'string' ? assetTypeLabel(prop) : prop
  }
})

// Type colors - distinct colors for each machine type
const typeColors = {
  // Machining
  'Mill': '#F44336',           // Red
  'Lathe': '#E91E63',          // Pink
  'Grinder': '#2196F3',        // Blue
  'Broach': '#00BCD4',         // Cyan
  'Hobbing': '#009688',        // Teal
  'Turn': '#FF5722',           // Deep Orange (Mill Turn, Vertical Turn)

  // Inspection & Measurement
  'CMM': '#9C27B0',            // Purple
  'Measuring': '#7B1FA2',      // Dark Purple
  'Eddy': '#673AB7',           // Deep Purple
  'Inspection': '#8BC34A',     // Light Green

  // Heat Treatment & Processing
  'Furnace': '#FF9800',        // Orange
  'Wash': '#4CAF50',           // Green
  'Wax': '#FFEB3B',            // Yellow

  // Automation
  'Robot': '#3F51B5',          // Indigo
  'Deburr': '#5C6BC0',         // Indigo Light

  // Welding
  'Welder': '#795548',         // Brown

  // IT/Network
  'PC': '#607D8B',             // Blue Grey
  'Printer': '#78909C',        // Blue Grey Light
  'Switch': '#546E7A',         // Blue Grey Dark
  'Access Point': '#455A64',   // Blue Grey Darker
  'IDF': '#37474F',            // Blue Grey Very Dark

  // Other
  'Saw': '#8D6E63',            // Brown Light
  'Press': '#A1887F',          // Brown Lighter
  'EDM': '#00ACC1',            // Cyan Dark
  'Drill': '#26A69A',          // Teal Light
  'CNC': '#66BB6A',            // Green Light
  'Assembly': '#CDDC39',       // Lime
  'Other': '#BDBDBD'           // Grey
}

function getTypeColor(typeName) {
  if (!typeName) return '#BDBDBD'
  for (const [key, color] of Object.entries(typeColors)) {
    if (typeName.toLowerCase().includes(key.toLowerCase())) {
      return color
    }
  }
  return '#BDBDBD'  // Grey for unknown types
}

// Get unique visible types from machines
const visibleTypes = computed(() => {
  const typeIds = new Set(props.machines.map(m => m.machinetypeid))
  return props.machinetypes.filter(t => typeIds.has(t.machinetypeid))
})

// Get unique visible asset types (for asset mode)
const visibleAssetTypes = computed(() => {
  const types = new Set(props.machines.map(m => m.assettype).filter(Boolean))
  const result = {}
  for (const t of types) {
    result[t] = getAssetTypeColor(t)
  }
  return result
})

// Legend swatches wear the same ring as the markers they stand for, so the key
// reads as a key. Without this the dot took a surface-colored border and a light
// color looked ringed in white next to its black-ringed marker.
function legendDotStyle(color) {
  return { background: color, borderColor: markerRingColor(props.theme) }
}

// Get visible subtypes when a type is selected
const visibleSubtypes = computed(() => {
  if (!props.selectedAssetType) return {}
  const subtypeIds = new Set(
    props.machines
      .map(m => getSubtypeId(m))
      .filter(id => id != null)
  )
  const result = {}
  for (const id of subtypeIds) {
    if (props.subtypeColors[id]) {
      result[id] = props.subtypeColors[id]
    }
  }
  return result
})

function initMap() {
  if (!mapContainer.value) return

  // Use canvas renderer for performance - renders all markers on a single <canvas>
  canvasRenderer = L.canvas({ padding: 0.5 })

  map = L.map(mapContainer.value, {
    crs: L.CRS.Simple,
    minZoom: -4,
    maxZoom: 2,
    attributionControl: false,
    renderer: canvasRenderer
  })

  const bounds = [[0, 0], [MAP_HEIGHT, MAP_WIDTH]]
  imageOverlay = L.imageOverlay(blueprintUrlFor(props.theme, drawnLevelId()), bounds)
  imageOverlay.addTo(map)

  // Set initial view - zoom out to show full floor plan
  const initialZoom = -2
  map.setView([MAP_HEIGHT / 2, MAP_WIDTH / 2], initialZoom)
  map.setMaxBounds(bounds)

  // Bound UNCONDITIONALLY, and handleMapClick ignores the click when picker mode
  // is off. Binding it only when pickerMode was true AT MOUNT meant a map that
  // becomes a picker later never got a click handler at all: the map editor
  // opens with nothing selected, so clicking to place a marker did nothing, for
  // the whole life of the page. The asset forms only worked because their picker
  // is mounted inside a modal that is already in picker mode.
  map.on('click', handleMapClick)

  if (props.pickerMode && props.initialPosition) {
    setPickerPosition(props.initialPosition.left, props.initialPosition.top)
  }

  // Existing markers are drawn in picker mode too: placing a marker relative to
  // the ones already on the floor is the whole task. The forms pass no markers,
  // so this costs them nothing.
  renderMarkers()
}

function handleMapClick(e) {
  if (!props.pickerMode) return

  const leafletY = e.latlng.lat
  const leafletX = e.latlng.lng

  // Convert back to database coordinates
  const dbLeft = Math.round(leafletX)
  const dbTop = Math.round(MAP_HEIGHT - leafletY)

  setPickerPosition(dbLeft, dbTop)
}

function setPickerPosition(left, top) {
  // Remove old picker marker
  if (pickerMarker) {
    pickerMarker.remove()
  }

  pickedPosition.value = { left, top }

  // Convert to Leaflet coordinates
  const leafletY = MAP_HEIGHT - top
  const leafletX = left

  const icon = L.divIcon({
    html: `<div class="picker-marker-dot"></div>`,
    iconSize: [16, 16],
    iconAnchor: [8, 8],
    className: 'picker-marker'
  })

  pickerMarker = L.marker([leafletY, leafletX], { icon, draggable: true })
  pickerMarker.addTo(map)

  // Allow dragging to fine-tune position
  pickerMarker.on('dragend', () => {
    const pos = pickerMarker.getLatLng()
    const newLeft = Math.round(pos.lng)
    const newTop = Math.round(MAP_HEIGHT - pos.lat)
    pickedPosition.value = { left: newLeft, top: newTop }
    emit('positionPicked', pickedPosition.value)
  })

  emit('positionPicked', pickedPosition.value)
}

function clearPosition() {
  if (pickerMarker) {
    pickerMarker.remove()
    pickerMarker = null
  }
  pickedPosition.value = null
  emit('positionPicked', null)
}

// Fetch the declared map overlays (ADR-010) and each overlay's per-asset
// decoration data, keyed by assetid for a fast join during rendering.
async function loadOverlays() {
  let declared
  try {
    const response = await api.get('/pluginui/map-overlays')
    declared = response.data.data || []
  } catch (err) {
    return
  }
  const decorations = []
  const legend = []
  const seen = new Set()
  for (const overlay of declared) {
    let items = []
    try {
      const endpoint = overlay.endpoint.replace(/^\/api(?=\/)/, '')
      const response = await api.get(endpoint)
      items = response.data.data || []
    } catch (err) {
      continue
    }
    const byAsset = new Map()
    for (const item of items) {
      byAsset.set(item.assetid, { color: item.color, label: item.label })
      // distinct legend entries (label + color) for overlays that opt in
      if (overlay.legend) {
        const key = `${item.label}|${item.color}`
        if (!seen.has(key)) {
          seen.add(key)
          legend.push({ label: item.label, color: item.color })
        }
      }
    }
    decorations.push({ style: overlay.style || 'badge', byAsset })
  }
  overlayDecorations.value = decorations
  overlayLegend.value = legend
  if (map) renderMarkers()
}

// Draw ring/badge decorations for one asset's marker.
function applyOverlays(item, leafletY, leafletX) {
  if (item.assetid == null) return
  overlayDecorations.value.forEach((overlay) => {
    const dec = overlay.byAsset.get(item.assetid)
    if (!dec) return
    if (overlay.style === 'ring') {
      const ring = L.circleMarker([leafletY, leafletX], {
        radius: 9, fill: false, color: dec.color, weight: 2,
        opacity: 0.9, interactive: false, renderer: canvasRenderer,
      })
      ring.addTo(map)
      overlayLayers.push(ring)
    } else {
      const badge = L.circleMarker([leafletY + 4, leafletX + 4], {
        radius: 3.5, fillColor: dec.color, color: '#fff', weight: 1,
        fillOpacity: 1, interactive: false, renderer: canvasRenderer,
      })
      badge.addTo(map)
      overlayLayers.push(badge)
    }
  })
}

function renderMarkers() {
  // Clear existing markers + overlay decoration layers
  markers.value.forEach(m => m.marker.remove())
  markers.value = []
  overlayLayers.forEach(layer => layer.remove())
  overlayLayers = []

  // Markers belong to ONE level (ADR-017). This is the drawing being shown, and
  // anything positioned against a different one is not drawn on it.
  const drawnLevel = drawnLevelId() ?? null

  props.machines.forEach(item => {
    if (item.mapx == null || item.mapy == null) return
    // A marker from another level, placed on THIS blueprint, looks entirely
    // correct and points at the wrong part of the building - so it is omitted
    // rather than approximated. A position with no level at all is omitted for
    // the same reason: the map editor lists both, badged, so they can be fixed
    // rather than silently misplaced here.
    if ((item.levelid ?? null) !== drawnLevel) return

    // Transform coordinates (database Y is top-down, Leaflet is bottom-up)
    const leafletY = MAP_HEIGHT - item.mapy
    const leafletX = item.mapx

    // Determine color based on mode
    let color, typeName, displayName

    if (props.assetTypeMode) {
      // Unified asset mode - use subtype colors when a type is selected
      if (props.selectedAssetType) {
        const subtypeId = getSubtypeId(item)
        color = (subtypeId && props.subtypeColors[subtypeId]) || UNSPECIFIED_COLOR
        typeName = (subtypeId && props.subtypeNames[subtypeId]) || item.assettype || ''
      } else {
        // Prefer the stored AssetType.color; fall back to the built-in map.
        color = item.assettypecolor || getAssetTypeColor(item.assettype)
        typeName = item.assettype || ''
      }
      displayName = item.displayname || item.name || item.assetnumber || 'Unknown'
      // Collapsed dual-bay pair: show the combined '2007 / 2008' label.
      if (item.dualpathpartner) {
        displayName = `${item.assetnumber} / ${item.dualpathpartner}`
      }
    } else {
      // Legacy machine mode
      typeName = item.machinetype || ''
      color = getTypeColor(typeName)
      displayName = item.alias || item.machinenumber || 'Unknown'
    }

    // Use circleMarker instead of divIcon marker - renders on canvas
    // for much better performance (no DOM element per marker)
    const marker = L.circleMarker([leafletY, leafletX], {
      radius: 6,
      fillColor: color,
      // Ring keyed on the theme's surface: dark on the white blueprint, light on
      // the dark one. A fixed white ring vanished against the light blueprint.
      color: markerRingColor(props.theme),
      weight: 1.25,
      fillOpacity: 1,
      renderer: canvasRenderer
    })

    // Build tooltip content
    let tooltipLines = [`<strong>${displayName}</strong>`]

    if (props.assetTypeMode) {
      tooltipLines.push(`<span style="color: #888;">${item.assettype || 'Unknown'}</span>`)

      if (item.primaryip) {
        tooltipLines.push(`<span style="color: #8cf;">IP: ${item.primaryip}</span>`)
      }
      if (item.typedata?.hostname) {
        tooltipLines.push(`<span style="color: #8cf;">${item.typedata.hostname}</span>`)
      }
    } else {
      const category = item.category?.toLowerCase() || ''

      if (typeName && typeName.toLowerCase() !== 'locationonly') {
        tooltipLines.push(`<span style="color: #888;">${typeName}</span>`)
      }

      if (item.vendor) {
        tooltipLines.push(`<span style="color: #aaa;">${item.vendor}${item.model ? ' ' + item.model : ''}</span>`)
      } else if (item.model) {
        tooltipLines.push(`<span style="color: #aaa;">${item.model}</span>`)
      }

      if (category === 'printer') {
        if (item.ipaddress) {
          tooltipLines.push(`<span style="color: #8cf;">IP: ${item.ipaddress}</span>`)
        }
        if (item.hostname) {
          tooltipLines.push(`<span style="color: #8cf;">${item.hostname}</span>`)
        }
      } else {
        if (item.connected_pc) {
          tooltipLines.push(`<span style="color: #fc8;">PC: ${item.connected_pc}</span>`)
        }
      }
    }

    if (item.businessunit) {
      tooltipLines.push(`<span style="color: #ccc;">${item.businessunit}</span>`)
    }

    const tooltipContent = tooltipLines.join('<br>')
    marker.bindTooltip(tooltipContent, {
      direction: 'top',
      offset: [0, -12],
      className: 'marker-tooltip'
    })

    // Hover is a glance; the click belongs to the consumer. No popup is bound:
    // MapView routes to the detail page on click, so a popup opened and was
    // discarded by the navigation in the same tick - it was never visible. In
    // the editor and the picker forms it DID render, where a 'View Details'
    // link only served to pull the user off an unsaved form.
    marker.on('click', () => emit('markerClick', item))

    marker.addTo(map)
    applyOverlays(item, leafletY, leafletX)

    // Build search data
    const searchData = props.assetTypeMode
      ? `${item.assetnumber} ${item.name} ${item.displayname} ${item.assettype} ${item.status} ${item.businessunit} ${item.typedata?.hostname || ''} ${item.primaryip || ''}`.toLowerCase()
      : `${item.machinenumber} ${item.alias} ${typeName} ${item.vendor} ${item.model} ${item.serialnumber} ${item.businessunit}`.toLowerCase()

    markers.value.push({
      marker,
      machine: item,
      searchData
    })
  })

  applyFilters()
}

function applyFilters() {
  const searchTerm = filters.value.search.toLowerCase()

  markers.value.forEach(({ marker, machine, searchData }) => {
    let visible = true

    if (filters.value.machinetype && machine.machinetypeid !== filters.value.machinetype) {
      visible = false
    }
    if (filters.value.businessunit && machine.businessunitid !== filters.value.businessunit) {
      visible = false
    }
    if (filters.value.status && machine.statusid !== filters.value.status) {
      visible = false
    }
    if (searchTerm && !searchData.includes(searchTerm)) {
      visible = false
    }

    marker.setStyle({ fillOpacity: visible ? 1 : 0.15, opacity: visible ? 1 : 0.15 })
  })
}

let searchTimeout = null
function debounceSearch() {
  clearTimeout(searchTimeout)
  searchTimeout = setTimeout(applyFilters, 300)
}

watch(() => props.machines, (newVal, oldVal) => {
  if (map && newVal !== oldVal) renderMarkers()
})

watch(() => props.theme, (newTheme) => {
  if (imageOverlay && map) {
    imageOverlay.setUrl(blueprintUrlFor(newTheme, drawnLevelId()))
    // Marker rings are keyed on the surface, so they have to be redrawn too.
    renderMarkers()
  }
})

// Switching level changes the drawing, the coordinate space AND which markers
// belong on it. All three move together: the size is what marker coordinates
// mean, so swapping the image without the bounds would place every marker
// against the wrong scale, and keeping the markers would show the previous
// floor's assets on this floor's plan.
watch(() => drawnLevelId(), (levelid) => {
  if (!map || !imageOverlay) return

  MAP_WIDTH = dimensionsFor(levelid).width
  MAP_HEIGHT = dimensionsFor(levelid).height
  const bounds = [[0, 0], [MAP_HEIGHT, MAP_WIDTH]]

  const url = blueprintUrlFor(props.theme, levelid)
  // A level with no blueprint is left blank rather than showing the previous
  // one, which would be a floor plan labelled as somewhere it is not.
  imageOverlay.setUrl(url || '')
  imageOverlay.setBounds(bounds)

  map.setMaxBounds(bounds)
  map.setView([MAP_HEIGHT / 2, MAP_WIDTH / 2], map.getZoom())

  renderMarkers()
  loadOverlays()
})

onMounted(async () => {
  // Load this facility's blueprint + dimensions before building the map so
  // bounds and coordinate math use the right size. Falls back to defaults.
  await loadMapConfig()
  MAP_WIDTH = dimensionsFor(drawnLevelId()).width
  MAP_HEIGHT = dimensionsFor(drawnLevelId()).height
  initMap()
  loadOverlays()
})

onUnmounted(() => {
  if (map) {
    map.remove()
    map = null
  }
})
</script>

<style scoped>
.shopfloor-map {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.map-controls {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.75rem;
  background: var(--bg-card);
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
  gap: 0.5rem;
}

.filters {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.filters select,
.filters input {
  padding: 0.625rem 1rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 1rem;
  background: var(--bg);
  color: var(--text);
}

.filters select {
  cursor: pointer;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23999' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  padding-right: 2.25rem;
  min-width: 160px;
}

.filters input {
  width: 280px;
}

.filters input::placeholder {
  color: var(--text-light);
  opacity: 0.7;
}

.filters input::placeholder {
  color: var(--text-light);
}

.legend {
  display: flex;
  gap: 1.5rem;
  flex-wrap: wrap;
  font-size: 1.125rem;
  color: var(--text);
}

.map-legend-bar {
  display: flex;
  gap: 1.5rem;
  flex-wrap: wrap;
  font-size: 1rem;
  color: var(--text);
  padding: 0.5rem 1rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 0.5rem;
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.legend-dot {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  /* Ring color is bound inline (legendDotStyle) to match the markers. */
  border: 1.5px solid var(--bg-card);
  box-shadow: 0 1px 4px rgba(0,0,0,0.4);
}

/* Overlay legend entries: a hollow ring, distinct from the solid type dots. */
.legend-dot.legend-ring {
  background: transparent;
  border-width: 3px;
  box-shadow: none;
}

.map-container {
  flex: 1;
  min-height: 600px;
  background: var(--bg);
  border: 1px solid var(--border);
}

.map-container.picker-active {
  cursor: crosshair;
}

.picker-controls {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  padding: 1rem;
  background: var(--warning);
  border-bottom: 1px solid var(--warning);
  font-size: 1.125rem;
}

.picker-message {
  font-weight: 500;
  color: #1a1a1a;
}

.picker-coords {
  font-family: monospace;
  background: var(--bg-card);
  color: var(--text);
  padding: 0.375rem 0.75rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  font-size: 1.125rem;
}

:deep(.machine-marker) {
  background: transparent !important;
  border: none !important;
}

:deep(.machine-marker-dot) {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.8);
  box-shadow: 0 1px 4px rgba(0,0,0,0.5);
}

:deep(.picker-marker-dot) {
  background: #ff0000;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid var(--border);
  box-shadow: 0 2px 6px rgba(0,0,0,0.5);
}

:deep(.marker-tooltip) {
  background: rgba(0, 0, 0, 0.92);
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: 14px 18px;
  font-size: 1.125rem;
  line-height: 1.7;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
  max-width: 400px;
}

:deep(.marker-tooltip::before) {
  border-top-color: rgba(0, 0, 0, 0.92);
}

/* Legend bar for asset type mode (no filters - parent handles them) */
.map-legend {
  display: flex;
  gap: 1.5rem;
  flex-wrap: wrap;
  padding: 0.75rem 1rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: 0.75rem;
  font-size: 0.875rem;
  color: var(--text);
}
</style>
