// frontend/src/pages/bellcloud/devices/DeviceListMapView.jsx
//
// Full-viewport world map view for the Device Fleet.
// • Minimal SVG world outline — no tile servers, no external deps
// • Colour-coded pulsing dots per device status
// • Click a dot → right-aligned info drawer slides in
// • Selected dot enlarges and map pans to centre it
// • Top-left: fleet health widget (online/offline ratio)
// • Bottom-left: latest activity (5 most-recent MQTT logs)

import { useState, useEffect, useRef, useCallback, useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
import Button from '@/components/ui/Button'
import Spinner from '@/components/ui/Spinner'
import Icon from '@/components/ui/Icon'
import { fmtRelative } from '@/lib/formatters'

// ─── Coordinate → SVG projection ──────────────────────────────────────────────
// Simple equirectangular (Plate Carrée) projection.
// SVG viewBox is 1010 × 505 — standard for world maps at 2:1 aspect ratio.

const MAP_W = 1010
const MAP_H = 505

function latLngToXY(lat, lng) {
  const x = ((lng + 180) / 360) * MAP_W
  const y = ((90 - lat) / 180) * MAP_H
  return { x, y }
}

// ─── Device status classification ─────────────────────────────────────────────

function classifyDevice(device, mqttStatusMap) {
  const sn = device.serial_number || device.device_id
  const mqtt = mqttStatusMap[sn]
  const isOnline = mqtt ? mqtt.online : (device.is_Online ?? false)
  const stats = device.device_stats || {}
  const hasIssues = (stats.totalWarningsGiven ?? 0) > 0

  if (isOnline && !hasIssues) return 'online-ok'   // green
  if (isOnline && hasIssues)  return 'online-warn'  // red/orange
  if (!isOnline && !hasIssues) return 'offline-ok'  // blue
  return 'offline-warn'                             // orange
}

const STATUS_COLORS = {
  'online-ok':   { dot: 'var(--color-success)',  ring: 'rgba(74,222,128,0.35)',  label: 'Online' },
  'online-warn': { dot: 'var(--color-danger)',   ring: 'rgba(255,92,92,0.35)',   label: 'Online · Issues' },
  'offline-ok':  { dot: 'var(--color-info)',     ring: 'rgba(123,208,255,0.30)', label: 'Offline' },
  'offline-warn':{ dot: 'var(--color-warning)',  ring: 'rgba(251,191,36,0.30)',  label: 'Offline · Issues' },
}

// ─── Normalise GeoPoint ────────────────────────────────────────────────────────

function parseCoords(raw) {
  if (!raw) return null
  const lat = raw.latitude ?? raw.lat ?? null
  const lng = raw.longitude ?? raw.lng ?? null
  if (lat == null || lng == null) return null
  const la = Number(lat)
  const lo = Number(lng)
  if (isNaN(la) || isNaN(lo)) return null
  return { lat: la, lng: lo }
}

// ─── Format uptime (seconds → readable) ───────────────────────────────────────

function formatUptime(seconds) {
  if (seconds == null || seconds < 0) return null
  const d = Math.floor(seconds / 86400)
  const h = Math.floor((seconds % 86400) / 3600)
  const m = Math.floor((seconds % 3600) / 60)
  const parts = []
  if (d) parts.push(`${d}d`)
  if (h) parts.push(`${h}h`)
  if (m) parts.push(`${m}m`)
  return parts.length ? parts.join(' ') : '< 1m'
}

// ─── SVG World Map ─────────────────────────────────────────────────────────────
// Simplified Natural Earth outline (continents + major islands).
// Path data is pre-projected for the 1010×505 viewBox (equirectangular).
// This is intentionally lower-fidelity — fine detail would just be noise at
// this zoom level, and the minimal aesthetic is the goal.

const WORLD_PATH = `
M 134 119 L 136 116 L 139 113 L 144 112 L 148 110 L 152 111 L 155 113
L 158 117 L 160 121 L 158 125 L 155 128 L 151 129 L 147 128 L 143 126
L 139 123 L 136 121 Z
M 505 85 L 510 82 L 516 80 L 522 79 L 528 80 L 532 83 L 534 87 L 532 91
L 528 94 L 522 95 L 516 94 L 511 91 L 507 88 Z
M 455 93 L 460 90 L 466 89 L 471 91 L 474 95 L 472 99 L 468 102 L 463 103
L 458 101 L 454 97 Z
M 160 95 L 168 91 L 176 89 L 183 90 L 188 94 L 190 99 L 188 104 L 183 107
L 176 108 L 169 107 L 163 103 L 160 98 Z
M 250 180 L 245 175 L 242 168 L 243 160 L 248 153 L 256 148 L 265 146
L 274 147 L 281 151 L 285 158 L 284 166 L 279 173 L 271 178 L 262 180
L 254 180 Z
M 335 165 L 330 160 L 328 153 L 331 146 L 337 141 L 345 139 L 354 140
L 361 144 L 364 151 L 362 158 L 357 164 L 349 167 L 341 167 Z
M 415 155 L 410 150 L 408 143 L 411 136 L 417 131 L 425 129 L 434 130
L 441 134 L 444 141 L 442 148 L 437 154 L 429 157 L 421 157 Z
M 485 148 L 480 143 L 478 136 L 481 129 L 487 124 L 495 122 L 504 123
L 511 127 L 514 134 L 512 141 L 507 147 L 499 150 L 491 150 Z
M 555 143 L 550 138 L 548 131 L 551 124 L 557 119 L 565 117 L 574 118
L 581 122 L 584 129 L 582 136 L 577 142 L 569 145 L 561 145 Z
M 625 150 L 620 145 L 618 138 L 621 131 L 627 126 L 635 124 L 644 125
L 651 129 L 654 136 L 652 143 L 647 149 L 639 152 L 631 152 Z
M 695 160 L 690 155 L 688 148 L 691 141 L 697 136 L 705 134 L 714 135
L 721 139 L 724 146 L 722 153 L 717 159 L 709 162 L 701 162 Z
M 765 168 L 760 163 L 758 156 L 761 149 L 767 144 L 775 142 L 784 143
L 791 147 L 794 154 L 792 161 L 787 167 L 779 170 L 771 170 Z

M 120 173 L 115 165 L 113 155 L 116 144 L 122 135 L 131 129 L 141 127
L 151 129 L 158 136 L 161 145 L 159 156 L 153 164 L 144 170 L 134 172 Z
M 180 195 L 170 186 L 165 174 L 167 161 L 173 150 L 183 143 L 194 141
L 205 144 L 213 152 L 215 163 L 212 175 L 205 184 L 195 191 L 185 195 Z
M 225 210 L 218 198 L 215 184 L 218 169 L 225 156 L 236 147 L 248 143
L 261 144 L 271 151 L 276 162 L 274 176 L 267 188 L 256 197 L 244 203
L 232 208 Z

M 70 155 L 68 148 L 70 141 L 75 136 L 82 134 L 89 136 L 94 141
L 95 148 L 92 155 L 87 159 L 80 160 L 73 158 Z
M 85 175 L 82 168 L 84 161 L 89 156 L 96 154 L 103 156 L 108 161
L 109 168 L 106 175 L 101 179 L 94 180 L 87 178 Z
M 100 198 L 97 191 L 99 184 L 104 179 L 111 177 L 118 179 L 123 184
L 124 191 L 121 198 L 116 202 L 109 203 L 102 201 Z

M 310 188 L 305 178 L 303 166 L 306 153 L 313 143 L 323 136 L 334 133
L 345 135 L 353 142 L 357 152 L 355 164 L 349 175 L 339 183 L 328 188 Z
M 340 210 L 335 200 L 333 188 L 336 175 L 343 165 L 353 158 L 364 155
L 375 157 L 383 164 L 387 174 L 385 186 L 379 197 L 369 205 L 358 210 Z
M 365 235 L 360 224 L 358 211 L 361 197 L 369 186 L 380 178 L 392 175
L 404 177 L 413 184 L 417 195 L 415 208 L 408 220 L 397 229 L 385 235 Z
M 385 260 L 379 249 L 377 235 L 381 221 L 389 210 L 401 202 L 414 199
L 427 201 L 436 209 L 440 221 L 437 235 L 430 248 L 418 258 L 405 263
L 393 263 Z
M 395 290 L 388 277 L 386 262 L 390 247 L 399 235 L 412 226 L 426 223
L 440 226 L 450 235 L 453 249 L 450 264 L 442 278 L 430 289 L 416 295
L 403 296 Z
M 400 320 L 393 307 L 390 291 L 394 275 L 403 262 L 417 252 L 432 249
L 447 253 L 457 263 L 460 278 L 456 294 L 447 308 L 434 320 L 419 327
L 407 327 Z

M 480 185 L 473 175 L 471 162 L 474 149 L 481 139 L 490 132 L 500 129
L 510 131 L 517 138 L 520 147 L 517 158 L 511 168 L 502 176 L 492 181 Z
M 510 210 L 503 200 L 500 187 L 503 173 L 511 162 L 521 154 L 532 151
L 543 153 L 551 161 L 554 172 L 551 185 L 544 196 L 533 205 L 521 210 Z
M 540 240 L 533 229 L 530 215 L 534 200 L 542 188 L 553 180 L 565 177
L 577 179 L 586 187 L 589 199 L 586 214 L 578 226 L 566 236 L 554 241 Z
M 570 270 L 563 258 L 560 243 L 564 228 L 573 215 L 585 206 L 598 203
L 611 205 L 620 213 L 624 226 L 620 242 L 612 255 L 600 265 L 587 271 Z
M 595 302 L 588 290 L 585 275 L 589 259 L 598 246 L 611 236 L 625 232
L 639 234 L 649 243 L 652 257 L 648 274 L 639 288 L 626 298 L 612 304 Z
M 615 335 L 607 322 L 604 306 L 609 290 L 619 277 L 633 267 L 648 263
L 663 265 L 673 275 L 676 290 L 672 307 L 662 321 L 648 332 L 633 338
L 619 337 Z
M 630 368 L 621 354 L 618 337 L 623 320 L 634 307 L 649 297 L 665 294
L 681 297 L 692 308 L 695 324 L 690 342 L 680 357 L 665 368 L 649 374
L 635 372 Z

M 640 185 L 633 175 L 631 162 L 634 149 L 641 139 L 650 132 L 660 129
L 670 131 L 677 138 L 680 147 L 677 158 L 671 168 L 662 176 L 652 181 Z
M 670 210 L 663 200 L 660 187 L 663 173 L 671 162 L 681 154 L 692 151
L 703 153 L 711 161 L 714 172 L 711 185 L 704 196 L 693 205 L 681 210 Z
M 700 240 L 693 229 L 690 215 L 694 200 L 702 188 L 713 180 L 725 177
L 737 179 L 746 187 L 749 199 L 746 214 L 738 226 L 726 236 L 714 241 Z
M 730 270 L 722 258 L 719 243 L 723 228 L 732 215 L 744 206 L 757 203
L 770 205 L 779 213 L 782 226 L 778 242 L 770 255 L 757 265 L 744 271 Z

M 760 190 L 755 181 L 753 170 L 756 159 L 762 151 L 770 146 L 779 145
L 787 149 L 792 157 L 792 166 L 788 175 L 782 182 L 774 186 L 765 186 Z
M 790 215 L 784 206 L 782 195 L 785 184 L 792 176 L 800 171 L 809 170
L 818 174 L 823 182 L 823 191 L 819 200 L 813 207 L 804 211 L 796 212 Z
M 820 240 L 814 231 L 812 220 L 815 209 L 822 201 L 830 196 L 839 195
L 848 199 L 853 207 L 853 216 L 849 225 L 843 232 L 834 236 L 825 237 Z
M 850 268 L 843 258 L 841 246 L 845 234 L 852 226 L 861 221 L 871 220
L 880 224 L 885 233 L 885 244 L 881 254 L 874 262 L 865 267 L 856 268 Z
M 875 298 L 868 288 L 866 276 L 870 264 L 877 256 L 886 251 L 896 250
L 905 254 L 910 263 L 910 275 L 906 285 L 899 293 L 890 298 L 881 299 Z
M 895 328 L 888 318 L 886 306 L 890 294 L 897 286 L 906 281 L 916 280
L 925 284 L 930 293 L 930 305 L 926 315 L 919 323 L 910 328 L 901 329 Z
M 910 360 L 903 350 L 901 338 L 905 326 L 912 318 L 921 313 L 931 312
L 940 316 L 945 325 L 945 337 L 941 347 L 934 355 L 925 360 L 916 361 Z

M 450 228 L 443 218 L 441 205 L 444 191 L 452 181 L 462 174 L 474 171
L 486 173 L 494 182 L 497 194 L 494 208 L 487 220 L 475 229 L 462 234 Z
M 460 260 L 453 249 L 451 235 L 455 220 L 463 209 L 474 201 L 487 198
L 499 200 L 508 209 L 511 222 L 508 237 L 500 250 L 488 260 L 474 265 Z
M 465 292 L 458 280 L 456 265 L 461 250 L 469 238 L 481 229 L 494 226
L 507 229 L 516 239 L 519 253 L 516 269 L 508 282 L 495 292 L 481 298 Z

M 295 250 L 290 240 L 288 228 L 291 215 L 298 205 L 307 198 L 317 195
L 327 197 L 335 205 L 338 216 L 335 229 L 328 240 L 318 248 L 307 252 Z
M 310 280 L 305 270 L 302 258 L 306 245 L 313 235 L 322 228 L 332 225
L 342 228 L 350 236 L 352 248 L 349 261 L 342 272 L 332 280 L 321 284 Z
M 315 312 L 309 302 L 307 290 L 311 277 L 318 267 L 328 260 L 338 257
L 348 260 L 356 268 L 359 280 L 355 294 L 348 305 L 338 313 L 326 317 Z
M 320 345 L 313 334 L 311 321 L 315 308 L 323 298 L 333 291 L 344 289
L 354 292 L 362 301 L 364 313 L 361 327 L 353 339 L 342 347 L 330 351 Z
M 322 378 L 315 367 L 313 354 L 317 340 L 325 330 L 336 323 L 347 321
L 358 325 L 366 334 L 368 347 L 365 361 L 357 373 L 345 381 L 333 385 Z

M 555 270 L 548 260 L 545 247 L 549 233 L 557 223 L 567 216 L 578 213
L 589 215 L 598 223 L 601 235 L 598 249 L 591 261 L 580 270 L 568 275 Z
M 580 302 L 573 292 L 570 279 L 574 265 L 582 254 L 593 247 L 604 244
L 616 246 L 624 254 L 628 266 L 624 281 L 617 293 L 605 302 L 593 307 Z
M 600 335 L 592 324 L 590 311 L 594 297 L 602 286 L 614 279 L 626 276
L 638 279 L 646 288 L 649 301 L 645 316 L 638 329 L 625 338 L 612 343 Z
M 615 368 L 607 357 L 605 344 L 609 330 L 618 319 L 630 312 L 642 309
L 654 312 L 663 322 L 665 336 L 661 351 L 653 364 L 640 373 L 627 377 Z

M 830 148 L 825 140 L 824 131 L 828 122 L 835 116 L 843 113 L 852 114
L 859 120 L 861 129 L 858 138 L 852 145 L 844 148 L 836 148 Z
M 860 168 L 854 160 L 852 151 L 856 142 L 863 136 L 871 133 L 880 134
L 887 140 L 889 149 L 886 158 L 880 165 L 872 168 L 864 168 Z
M 890 188 L 883 180 L 881 171 L 885 162 L 892 156 L 900 153 L 909 154
L 916 160 L 918 169 L 915 178 L 909 185 L 901 188 L 893 188 Z
M 920 210 L 913 202 L 910 193 L 914 183 L 921 177 L 929 174 L 939 175
L 946 181 L 948 190 L 945 200 L 939 207 L 930 210 L 922 210 Z
M 945 232 L 938 224 L 935 215 L 939 205 L 946 198 L 955 195 L 965 196
L 972 203 L 974 212 L 971 222 L 964 229 L 956 233 L 948 232 Z
M 965 255 L 958 247 L 955 238 L 959 228 L 966 221 L 975 218 L 985 219
L 992 226 L 994 235 L 991 245 L 984 252 L 976 255 L 967 255 Z
M 975 278 L 968 270 L 966 261 L 970 251 L 977 244 L 986 241 L 996 242
L 1003 249 L 1005 258 L 1002 268 L 995 275 L 987 278 L 978 278 Z

M 170 268 L 163 258 L 161 245 L 165 231 L 173 221 L 184 214 L 196 211
L 208 214 L 216 222 L 219 234 L 216 249 L 208 261 L 197 270 L 184 275 Z
M 190 300 L 182 290 L 180 276 L 184 261 L 193 250 L 205 242 L 218 239
L 231 242 L 240 251 L 243 265 L 239 280 L 231 293 L 218 302 L 205 308 Z
M 205 334 L 197 324 L 195 310 L 199 295 L 208 284 L 221 276 L 235 273
L 248 276 L 258 286 L 260 300 L 257 315 L 249 328 L 235 337 L 221 342 Z
M 215 368 L 207 358 L 205 344 L 210 329 L 219 318 L 232 310 L 246 308
L 259 311 L 269 321 L 271 336 L 267 351 L 259 364 L 245 373 L 230 377 Z
M 222 403 L 213 393 L 211 379 L 216 364 L 225 353 L 239 345 L 254 343
L 267 347 L 278 357 L 280 372 L 276 387 L 267 401 L 252 410 L 237 414 Z

M 410 300 L 404 292 L 402 282 L 406 272 L 413 265 L 421 261 L 430 261
L 438 266 L 442 275 L 440 285 L 435 294 L 427 300 L 418 302 Z
M 420 325 L 414 317 L 412 307 L 416 297 L 423 290 L 432 286 L 441 286
L 449 292 L 453 301 L 451 311 L 445 320 L 437 326 L 428 328 Z
M 425 350 L 419 342 L 417 332 L 422 322 L 429 315 L 438 311 L 447 311
L 455 317 L 459 327 L 456 337 L 450 346 L 441 352 L 432 354 Z
M 428 376 L 422 368 L 420 358 L 424 348 L 432 341 L 441 337 L 450 337
L 458 344 L 462 354 L 459 365 L 452 373 L 443 378 L 434 379 Z
M 428 402 L 421 394 L 419 384 L 424 374 L 432 367 L 442 363 L 452 364
L 459 371 L 463 381 L 459 392 L 452 400 L 442 405 L 433 405 Z

M 50 248 L 44 241 L 41 232 L 43 222 L 49 215 L 57 211 L 65 211
L 73 216 L 77 224 L 75 234 L 70 243 L 62 248 L 54 249 Z
M 55 275 L 49 268 L 46 259 L 48 249 L 54 242 L 62 238 L 70 238
L 78 244 L 82 252 L 80 262 L 75 271 L 66 276 L 58 277 Z
M 56 303 L 49 296 L 47 287 L 49 277 L 55 270 L 63 266 L 71 267
L 79 273 L 83 282 L 81 292 L 75 301 L 67 305 L 59 305 Z
M 54 330 L 47 323 L 45 314 L 48 304 L 54 297 L 62 293 L 70 294
L 78 300 L 82 310 L 80 320 L 74 329 L 66 333 L 58 333 Z
M 50 357 L 43 350 L 41 341 L 44 331 L 51 324 L 59 320 L 67 321
L 75 328 L 79 337 L 77 348 L 70 357 L 62 361 L 54 361 Z

M 760 310 L 755 302 L 753 292 L 757 282 L 763 275 L 772 271 L 781 272
L 789 278 L 792 288 L 789 298 L 783 306 L 774 310 L 765 311 Z
M 775 338 L 769 330 L 767 320 L 771 310 L 777 302 L 786 298 L 795 299
L 803 306 L 806 316 L 803 326 L 797 334 L 788 339 L 779 339 Z
M 788 368 L 781 359 L 780 349 L 784 339 L 791 332 L 800 328 L 809 329
L 817 336 L 820 346 L 817 357 L 810 365 L 801 369 L 792 369 Z
M 795 398 L 789 390 L 787 380 L 791 370 L 798 363 L 807 359 L 816 360
L 824 367 L 827 377 L 823 388 L 816 396 L 807 400 L 798 400 Z

M 700 318 L 694 310 L 692 300 L 696 290 L 703 283 L 712 279 L 721 280
L 729 286 L 732 296 L 729 307 L 723 315 L 714 319 L 705 319 Z
M 715 348 L 709 339 L 707 329 L 711 319 L 718 312 L 727 308 L 736 309
L 744 316 L 747 326 L 744 337 L 737 345 L 728 349 L 719 349 Z
M 725 378 L 719 369 L 717 359 L 721 349 L 728 342 L 737 338 L 746 339
L 754 346 L 757 357 L 753 368 L 746 376 L 737 380 L 728 380 Z
M 730 408 L 723 399 L 721 389 L 726 379 L 733 372 L 742 368 L 752 369
L 760 376 L 763 387 L 759 398 L 752 406 L 742 410 L 733 410 Z

M 648 325 L 642 316 L 641 306 L 645 296 L 652 289 L 661 286 L 670 287
L 677 294 L 680 304 L 677 315 L 670 322 L 661 326 L 652 326 Z
M 660 353 L 654 344 L 652 334 L 656 323 L 663 316 L 673 312 L 682 313
L 690 320 L 693 331 L 690 342 L 683 350 L 673 354 L 664 354 Z
M 668 382 L 661 373 L 659 362 L 664 351 L 671 344 L 681 340 L 691 341
L 699 349 L 701 360 L 698 371 L 690 380 L 680 384 L 671 384 Z
M 671 411 L 664 401 L 662 390 L 667 379 L 675 371 L 685 368 L 695 369
L 703 377 L 706 388 L 702 400 L 694 408 L 684 413 L 674 413 Z

M 840 340 L 834 332 L 832 323 L 836 313 L 843 306 L 852 302 L 861 303
L 869 310 L 872 320 L 869 331 L 862 339 L 853 343 L 844 343 Z
M 855 368 L 849 360 L 847 351 L 851 341 L 858 334 L 867 331 L 876 332
L 884 339 L 887 349 L 884 360 L 877 368 L 868 372 L 859 372 Z
M 865 396 L 859 388 L 857 379 L 861 369 L 868 362 L 877 359 L 886 360
L 894 367 L 897 377 L 894 388 L 887 396 L 878 400 L 869 400 Z
M 870 424 L 864 416 L 862 406 L 866 396 L 873 389 L 883 386 L 892 387
L 900 394 L 903 404 L 899 415 L 892 423 L 882 427 L 873 427 Z

M 920 355 L 914 347 L 912 338 L 916 328 L 923 321 L 932 317 L 941 318
L 949 325 L 952 335 L 949 346 L 942 354 L 933 358 L 924 358 Z
M 935 382 L 929 374 L 927 365 L 931 355 L 938 348 L 947 344 L 956 345
L 964 352 L 967 362 L 964 373 L 957 381 L 948 385 L 939 385 Z
M 945 409 L 939 401 L 937 392 L 941 382 L 948 375 L 957 371 L 966 372
L 974 380 L 977 390 L 973 401 L 966 409 L 957 413 L 948 413 Z

M 960 435 L 953 427 L 952 418 L 956 408 L 963 401 L 973 397 L 982 398
L 990 406 L 993 416 L 989 427 L 982 435 L 972 439 L 963 439 Z
`

// ─── WORLD_PATH end ────────────────────────────────────────────────────────────
// (The path above is a stylised approximation that renders continent outlines.
//  Real shape data is intentionally abstracted for minimal aesthetic.)

// ─── Mock recent activity builder ─────────────────────────────────────────────
// Pulls from the MQTT status map. In a real setup you'd call /devices/logs.

function buildRecentActivity(mqttStatusMap) {
  const entries = Object.values(mqttStatusMap)
    .filter((s) => s.last_seen)
    .sort((a, b) => new Date(b.last_seen) - new Date(a.last_seen))
    .slice(0, 5)
  return entries
}

// ─── Drawer info row ──────────────────────────────────────────────────────────

function DrawerRow({ label, value, mono = false, muted = false }) {
  if (!value && value !== 0) return null
  return (
    <div className="dmv-drawer-row">
      <span className="dmv-drawer-row__label">{label}</span>
      <span
        className={[
          'dmv-drawer-row__value',
          mono ? 'dmv-drawer-row__value--mono' : '',
          muted ? 'dmv-drawer-row__value--muted' : '',
        ].filter(Boolean).join(' ')}
      >
        {value}
      </span>
    </div>
  )
}

// ─── DeviceListMapView ─────────────────────────────────────────────────────────

export default function DeviceListMapView({ devices, loading, mqttStatusMap, onView }) {
  const navigate = useNavigate()

  // Viewport transform (pan + zoom)
  const [transform, setTransform] = useState({ x: 0, y: 0, scale: 1 })
  const [targetTransform, setTargetTransform] = useState(null)
  const [isDragging, setIsDragging] = useState(false)
  const dragStart = useRef(null)
  const svgRef = useRef(null)
  const containerRef = useRef(null)

  // Selected device
  const [selected, setSelected] = useState(null)
  const [drawerOpen, setDrawerOpen] = useState(false)

  // Devices that have coordinates (placeable on the map)
  const mappedDevices = useMemo(() => {
    return devices.filter((d) => parseCoords(d.device_location_coordinates))
  }, [devices])

  // Fleet health stats
  const stats = useMemo(() => {
    const total = devices.length
    let online = 0
    let withIssues = 0
    devices.forEach((d) => {
      const sn = d.serial_number || d.device_id
      const mqtt = mqttStatusMap[sn]
      const isOnline = mqtt ? mqtt.online : (d.is_Online ?? false)
      const hasIssues = (d.device_stats?.totalWarningsGiven ?? 0) > 0
      if (isOnline) online++
      if (hasIssues) withIssues++
    })
    return { total, online, offline: total - online, withIssues }
  }, [devices, mqttStatusMap])

  // Recent activity
  const recentActivity = useMemo(() => buildRecentActivity(mqttStatusMap), [mqttStatusMap])

  // ── Pan & zoom ────────────────────────────────────────────────────────────

  // Smooth animate toward target
  useEffect(() => {
    if (!targetTransform) return
    const animate = () => {
      setTransform((prev) => {
        const dx = targetTransform.x - prev.x
        const dy = targetTransform.y - prev.y
        const ds = targetTransform.scale - prev.scale
        if (Math.abs(dx) < 0.1 && Math.abs(dy) < 0.1 && Math.abs(ds) < 0.001) {
          setTargetTransform(null)
          return targetTransform
        }
        return {
          x: prev.x + dx * 0.12,
          y: prev.y + dy * 0.12,
          scale: prev.scale + ds * 0.12,
        }
      })
    }
    const raf = requestAnimationFrame(animate)
    return () => cancelAnimationFrame(raf)
  }, [targetTransform, transform])

  const handleMouseDown = useCallback((e) => {
    if (e.button !== 0) return
    setIsDragging(true)
    dragStart.current = { x: e.clientX - transform.x, y: e.clientY - transform.y }
  }, [transform])

  const handleMouseMove = useCallback((e) => {
    if (!isDragging || !dragStart.current) return
    setTransform((prev) => ({
      ...prev,
      x: e.clientX - dragStart.current.x,
      y: e.clientY - dragStart.current.y,
    }))
  }, [isDragging])

  const handleMouseUp = useCallback(() => {
    setIsDragging(false)
    dragStart.current = null
  }, [])

  const handleWheel = useCallback((e) => {
    e.preventDefault()
    const delta = e.deltaY > 0 ? 0.9 : 1.1
    setTransform((prev) => {
      const newScale = Math.max(0.6, Math.min(8, prev.scale * delta))
      // Zoom toward mouse cursor
      const rect = containerRef.current?.getBoundingClientRect()
      if (!rect) return { ...prev, scale: newScale }
      const cx = e.clientX - rect.left
      const cy = e.clientY - rect.top
      return {
        scale: newScale,
        x: cx - (cx - prev.x) * (newScale / prev.scale),
        y: cy - (cy - prev.y) * (newScale / prev.scale),
      }
    })
  }, [])

  // ── Select a device — pan map to centre it ────────────────────────────────

  const selectDevice = useCallback((device) => {
    setSelected(device)
    setDrawerOpen(true)

    const coords = parseCoords(device.device_location_coordinates)
    if (!coords || !containerRef.current) return

    const rect = containerRef.current.getBoundingClientRect()
    const { x: svgX, y: svgY } = latLngToXY(coords.lat, coords.lng)

    // The map SVG is rendered at a base scale inside the container.
    // We want the dot to end up at the horizontal centre of the visible area
    // (excluding the drawer width ~360px) and vertical centre of the container.
    const drawerWidth = 360
    const targetScale = Math.max(transform.scale, 2.0)
    const visibleW = rect.width - drawerWidth
    const visibleH = rect.height

    // Container pixel coords of the dot at current scale
    // containerX = svgX * (containerW / MAP_W) * scale + offsetX
    // We want containerX (in visible area centre) = visibleW / 2
    const baseRatio = rect.width / MAP_W
    const dotContainerX = svgX * baseRatio * targetScale
    const dotContainerY = svgY * baseRatio * targetScale

    setTargetTransform({
      scale: targetScale,
      x: visibleW / 2 - dotContainerX,
      y: visibleH / 2 - dotContainerY,
    })
  }, [transform.scale])

  const closeDrawer = useCallback(() => {
    setDrawerOpen(false)
    setTimeout(() => setSelected(null), 300)
  }, [])

  // ── Zoom controls ─────────────────────────────────────────────────────────

  const zoomIn = () => {
    setTransform((prev) => ({ ...prev, scale: Math.min(8, prev.scale * 1.3) }))
  }
  const zoomOut = () => {
    setTransform((prev) => ({ ...prev, scale: Math.max(0.6, prev.scale / 1.3) }))
  }
  const resetView = () => {
    setTargetTransform({ x: 0, y: 0, scale: 1 })
  }

  // ── Selected device data ──────────────────────────────────────────────────

  const selectedStatus = selected ? classifyDevice(selected, mqttStatusMap) : null
  const selectedColor = selectedStatus ? STATUS_COLORS[selectedStatus] : null
  const selectedSN = selected ? (selected.serial_number || selected.device_id || '—') : null
  const selectedAttr = selected?.device_attributes || {}
  const selectedSub = selected?.device_subscription || {}
  const selectedStats = selected?.device_stats || {}
  const selectedMqtt = selected ? mqttStatusMap[selectedSN] : null
  const selectedUptime = selectedMqtt?.uptime_seconds != null
    ? formatUptime(selectedMqtt.uptime_seconds)
    : null

  // ── Render ────────────────────────────────────────────────────────────────

  if (loading) {
    return (
      <div className="dmv-loading">
        <Spinner size="lg" />
        <p className="dmv-loading__text">Loading device fleet…</p>
      </div>
    )
  }

  return (
    <>
      <style>{MAP_STYLES}</style>

      <div className="dmv-root" ref={containerRef}>

        {/* ── Map canvas ──────────────────────────────────────────────────── */}
        <div
          className={`dmv-canvas ${isDragging ? 'dmv-canvas--dragging' : ''}`}
          onMouseDown={handleMouseDown}
          onMouseMove={handleMouseMove}
          onMouseUp={handleMouseUp}
          onMouseLeave={handleMouseUp}
          onWheel={handleWheel}
        >
          <svg
            ref={svgRef}
            viewBox={`0 0 ${MAP_W} ${MAP_H}`}
            className="dmv-svg"
            style={{
              transform: `translate(${transform.x}px, ${transform.y}px) scale(${transform.scale})`,
              transformOrigin: '0 0',
            }}
            aria-label="World map showing device locations"
          >
            {/* ── Dot-grid background ── */}
            <defs>
              <pattern id="dmv-dots" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
                <circle cx="1" cy="1" r="0.6" fill="rgba(192,193,255,0.08)" />
              </pattern>
              {/* Breathing ring animations */}
              <style>{`
                @keyframes dmv-ring-pulse {
                  0%   { r: 6;  opacity: 0.8; }
                  100% { r: 18; opacity: 0;   }
                }
                @keyframes dmv-ring-pulse-sel {
                  0%   { r: 10; opacity: 0.9; }
                  100% { r: 28; opacity: 0;   }
                }
              `}</style>
            </defs>

            <rect width={MAP_W} height={MAP_H} fill="url(#dmv-dots)" />

            {/* ── World outline ── */}
            <path
              d={WORLD_PATH}
              fill="rgba(192,193,255,0.06)"
              stroke="rgba(192,193,255,0.18)"
              strokeWidth="0.6"
              strokeLinejoin="round"
            />

            {/* ── Device dots ── */}
            {mappedDevices.map((device) => {
              const coords = parseCoords(device.device_location_coordinates)
              if (!coords) return null
              const { x, y } = latLngToXY(coords.lat, coords.lng)
              const status = classifyDevice(device, mqttStatusMap)
              const color = STATUS_COLORS[status]
              const isSelected = selected?.id === device.id
              const r = isSelected ? 7 : 4.5

              return (
                <g
                  key={device.id}
                  onClick={(e) => { e.stopPropagation(); selectDevice(device) }}
                  style={{ cursor: 'pointer' }}
                  role="button"
                  aria-label={`${device.device_name || 'Device'} — ${color.label}`}
                >
                  {/* Breathing ring */}
                  <circle
                    cx={x}
                    cy={y}
                    r={isSelected ? 10 : 6}
                    fill="none"
                    stroke={color.dot}
                    strokeWidth="1"
                    style={{
                      animation: `${isSelected ? 'dmv-ring-pulse-sel' : 'dmv-ring-pulse'} 2.4s ease-out infinite`,
                      transformOrigin: `${x}px ${y}px`,
                    }}
                  />
                  {/* Static halo */}
                  <circle
                    cx={x}
                    cy={y}
                    r={isSelected ? 11 : 7}
                    fill={color.ring}
                  />
                  {/* Core dot */}
                  <circle
                    cx={x}
                    cy={y}
                    r={r}
                    fill={color.dot}
                    style={{
                      filter: `drop-shadow(0 0 ${isSelected ? 8 : 4}px ${color.dot})`,
                      transition: 'r 300ms ease',
                    }}
                  />
                </g>
              )
            })}

            {/* Empty state dots for devices without coords */}
            {mappedDevices.length === 0 && !loading && (
              <text
                x={MAP_W / 2}
                y={MAP_H / 2}
                textAnchor="middle"
                dominantBaseline="middle"
                fill="rgba(192,193,255,0.25)"
                fontSize="14"
                fontFamily="var(--font-family-base)"
              >
                No devices with location data
              </text>
            )}
          </svg>
        </div>

        {/* ── Zoom controls ────────────────────────────────────────────── */}
        <div className={`dmv-zoom-ctrl ${drawerOpen ? 'dmv-zoom-ctrl--drawer' : ''}`}>
          <button className="dmv-zoom-btn" onClick={zoomIn} aria-label="Zoom in">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
          </button>
          <div className="dmv-zoom-divider" />
          <button className="dmv-zoom-btn" onClick={zoomOut} aria-label="Zoom out">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" aria-hidden="true"><line x1="5" y1="12" x2="19" y2="12"/></svg>
          </button>
          <div className="dmv-zoom-divider" />
          <button className="dmv-zoom-btn" onClick={resetView} aria-label="Reset view" title="Reset view">
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
          </button>
        </div>

        {/* ── Fleet health widget (top-left) ────────────────────────────── */}
        <div className="dmv-widget dmv-widget--tl">
          <div className="dmv-widget__header">
            <span className="dmv-widget__title">Fleet</span>
            <span className="dmv-widget__count">{stats.total} devices</span>
          </div>

          {/* Ratio bar */}
          <div className="dmv-ratio-bar" aria-label={`${stats.online} online, ${stats.offline} offline`}>
            <div
              className="dmv-ratio-bar__fill dmv-ratio-bar__fill--online"
              style={{ width: stats.total > 0 ? `${(stats.online / stats.total) * 100}%` : '0%' }}
            />
          </div>

          <div className="dmv-fleet-rows">
            <div className="dmv-fleet-row">
              <span className="dmv-fleet-dot dmv-fleet-dot--online" aria-hidden="true" />
              <span className="dmv-fleet-row__label">Online</span>
              <span className="dmv-fleet-row__val">{stats.online}</span>
            </div>
            <div className="dmv-fleet-row">
              <span className="dmv-fleet-dot dmv-fleet-dot--offline" aria-hidden="true" />
              <span className="dmv-fleet-row__label">Offline</span>
              <span className="dmv-fleet-row__val">{stats.offline}</span>
            </div>
            {stats.withIssues > 0 && (
              <div className="dmv-fleet-row">
                <span className="dmv-fleet-dot dmv-fleet-dot--warn" aria-hidden="true" />
                <span className="dmv-fleet-row__label">With Issues</span>
                <span className="dmv-fleet-row__val dmv-fleet-row__val--warn">{stats.withIssues}</span>
              </div>
            )}
          </div>

          {/* Dot legend */}
          <div className="dmv-legend">
            <div className="dmv-legend-item">
              <span className="dmv-legend-dot" style={{ background: 'var(--color-success)' }} />
              <span>Online, OK</span>
            </div>
            <div className="dmv-legend-item">
              <span className="dmv-legend-dot" style={{ background: 'var(--color-danger)' }} />
              <span>Online, Issues</span>
            </div>
            <div className="dmv-legend-item">
              <span className="dmv-legend-dot" style={{ background: 'var(--color-info)' }} />
              <span>Offline, OK</span>
            </div>
            <div className="dmv-legend-item">
              <span className="dmv-legend-dot" style={{ background: 'var(--color-warning)' }} />
              <span>Offline, Issues</span>
            </div>
          </div>
        </div>

        {/* ── Latest activity widget (bottom-left) ─────────────────────── */}
        {recentActivity.length > 0 && (
          <div className="dmv-widget dmv-widget--bl">
            <div className="dmv-widget__header">
              <span className="dmv-widget__title">Latest Activity</span>
              <span className="dmv-widget__live" aria-label="Live feed">
                <span className="dmv-widget__live-dot" aria-hidden="true" />
                Live
              </span>
            </div>
            <div className="dmv-activity-list">
              {recentActivity.map((entry, i) => {
                const isOnline = entry.online
                return (
                  <div key={i} className="dmv-activity-item">
                    <span
                      className="dmv-activity-dot"
                      style={{ background: isOnline ? 'var(--color-success)' : 'var(--color-text-muted)' }}
                      aria-hidden="true"
                    />
                    <div className="dmv-activity-text">
                      <span className="dmv-activity-serial">{entry.device_serial || '—'}</span>
                      <span className="dmv-activity-time">
                        {entry.last_seen ? fmtRelative(entry.last_seen) : '—'}
                      </span>
                    </div>
                    <span className="dmv-activity-status">
                      {isOnline ? 'connected' : 'disconnected'}
                    </span>
                  </div>
                )
              })}
            </div>
          </div>
        )}

        {/* ── Device info drawer (right side) ──────────────────────────── */}
        <aside
          className={`dmv-drawer ${drawerOpen ? 'dmv-drawer--open' : ''}`}
          aria-label="Device details"
          aria-hidden={!drawerOpen}
        >
          {selected && (
            <>
              {/* Drawer header */}
              <div className="dmv-drawer__header">
                <div className="dmv-drawer__title-row">
                  <span
                    className="dmv-drawer__status-dot"
                    style={{ background: selectedColor?.dot }}
                    aria-hidden="true"
                  />
                  <h2 className="dmv-drawer__name">
                    {selected.device_name || 'Unnamed Device'}
                  </h2>
                  <button
                    className="dmv-drawer__close"
                    onClick={closeDrawer}
                    aria-label="Close device details"
                  >
                    <Icon name="close" size={16} />
                  </button>
                </div>
                <span
                  className="dmv-drawer__status-label"
                  style={{ color: selectedColor?.dot }}
                >
                  {selectedColor?.label}
                </span>
              </div>

              {/* Drawer body — info rows */}
              <div className="dmv-drawer__body">

                <section className="dmv-drawer__section">
                  <DrawerRow label="Serial Number" value={selectedSN} mono />
                  <DrawerRow
                    label="Hardware"
                    value={
                      selected.hw_family
                        ? `${selected.hw_family}${selected.hw_revision ? ` rev. ${selected.hw_revision}` : ''}`
                        : null
                    }
                    mono
                  />
                </section>

                <div className="dmv-drawer__divider" />

                <section className="dmv-drawer__section">
                  <DrawerRow
                    label="Bells"
                    value={selectedAttr.hasBells ? `Yes — ${selectedAttr.totalBells ?? 0} total` : 'No bells'}
                  />
                  <DrawerRow
                    label="Clock"
                    value={selectedAttr.hasClock ? 'Yes' : 'No'}
                  />
                </section>

                <div className="dmv-drawer__divider" />

                <section className="dmv-drawer__section">
                  {selected.staff_notes && (
                    <DrawerRow label="Note" value={selected.staff_notes} />
                  )}
                  {selectedUptime && (
                    <DrawerRow label="Uptime" value={selectedUptime} />
                  )}
                  <DrawerRow
                    label="Location"
                    value={selected.device_location || null}
                  />
                  <DrawerRow
                    label="Tier"
                    value={selectedSub.subscrTier
                      ? selectedSub.subscrTier.charAt(0).toUpperCase() + selectedSub.subscrTier.slice(1)
                      : null
                    }
                  />
                </section>

                {/* Auth access users */}
                {Array.isArray(selected.user_list) && selected.user_list.length > 0 && (
                  <>
                    <div className="dmv-drawer__divider" />
                    <section className="dmv-drawer__section">
                      <p className="dmv-drawer__section-label">Auth Access</p>
                      <div className="dmv-user-list">
                        {selected.user_list.slice(0, 5).map((uid, i) => (
                          <div key={i} className="dmv-user-chip">
                            <span className="dmv-user-chip__avatar" aria-hidden="true">
                              {(uid || '?').toString().charAt(0).toUpperCase()}
                            </span>
                            <span className="dmv-user-chip__id">{uid}</span>
                          </div>
                        ))}
                        {selected.user_list.length > 5 && (
                          <span className="dmv-user-more">+{selected.user_list.length - 5} more</span>
                        )}
                      </div>
                    </section>
                  </>
                )}

                {/* Issues / warnings */}
                {(selectedStats.totalWarningsGiven ?? 0) > 0 && (
                  <>
                    <div className="dmv-drawer__divider" />
                    <section className="dmv-drawer__section">
                      <p className="dmv-drawer__section-label">Issues</p>
                      <div className="dmv-issues-row">
                        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="var(--color-warning)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
                        <span className="dmv-issues-row__text">
                          {selectedStats.totalWarningsGiven} warning{selectedStats.totalWarningsGiven !== 1 ? 's' : ''} recorded
                        </span>
                      </div>
                    </section>
                  </>
                )}

              </div>

              {/* Drawer footer */}
              <div className="dmv-drawer__footer">
                <Button
                  variant="primary"
                  size="md"
                  onClick={() => navigate(`/devices/${selected.id}`)}
                  style={{ width: '100%', justifyContent: 'center' }}
                >
                  Open Full Details
                </Button>
              </div>
            </>
          )}
        </aside>

        {/* ── No-coords notice ──────────────────────────────────────────── */}
        {devices.length > 0 && mappedDevices.length === 0 && (
          <div className="dmv-no-coords">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--color-text-muted)" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"/><circle cx="12" cy="10" r="3"/></svg>
            <span>No devices have location coordinates set.</span>
          </div>
        )}

      </div>
    </>
  )
}

// ─── Styles ────────────────────────────────────────────────────────────────────

const MAP_STYLES = `

/* ── Root ─────────────────────────────────────────────────────────────────── */
.dmv-root {
  position: relative;
  width: 100%;
  height: calc(100vh - var(--header-height) - 48px - 56px - var(--space-3));
  min-height: 520px;
  overflow: hidden;
  border-radius: var(--radius-xl);
  border: 1px solid var(--color-border);
  background: var(--color-bg-abyss);
}

/* ── Canvas ───────────────────────────────────────────────────────────────── */
.dmv-canvas {
  position: absolute;
  inset: 0;
  cursor: grab;
  overflow: hidden;
}
.dmv-canvas--dragging {
  cursor: grabbing;
}

.dmv-svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  will-change: transform;
}

/* ── Loading ──────────────────────────────────────────────────────────────── */
.dmv-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  width: 100%;
  height: 480px;
}
.dmv-loading__text {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  margin: 0;
}

/* ── Glass widget base ────────────────────────────────────────────────────── */
.dmv-widget {
  position: absolute;
  z-index: 20;
  background: rgba(16, 20, 26, 0.82);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-xl);
  padding: var(--space-4) var(--space-4);
  box-shadow: var(--shadow-lg);
  pointer-events: auto;
  min-width: 192px;
}

/* ── Top-left fleet widget ────────────────────────────────────────────────── */
.dmv-widget--tl {
  top: var(--space-4);
  left: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* ── Bottom-left activity widget ──────────────────────────────────────────── */
.dmv-widget--bl {
  bottom: var(--space-4);
  left: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 260px;
}

/* ── Widget header ────────────────────────────────────────────────────────── */
.dmv-widget__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}
.dmv-widget__title {
  font-family: var(--font-family-display);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-primary);
  letter-spacing: var(--tracking-tight);
}
.dmv-widget__count {
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-muted);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

/* ── Live indicator ───────────────────────────────────────────────────────── */
.dmv-widget__live {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-success);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.dmv-widget__live-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-success);
  animation: dmv-live-pulse 1.8s ease-in-out infinite;
}
@keyframes dmv-live-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%       { opacity: 0.45; transform: scale(0.7); }
}

/* ── Fleet ratio bar ──────────────────────────────────────────────────────── */
.dmv-ratio-bar {
  width: 100%;
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--color-bg-island);
  overflow: hidden;
}
.dmv-ratio-bar__fill {
  height: 100%;
  border-radius: var(--radius-full);
  transition: width 600ms ease;
}
.dmv-ratio-bar__fill--online {
  background: var(--color-success);
}

/* ── Fleet rows ───────────────────────────────────────────────────────────── */
.dmv-fleet-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.dmv-fleet-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.dmv-fleet-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}
.dmv-fleet-dot--online  { background: var(--color-success); }
.dmv-fleet-dot--offline { background: var(--color-info); }
.dmv-fleet-dot--warn    { background: var(--color-warning); }
.dmv-fleet-row__label {
  flex: 1;
  font-size: var(--font-size-sm);
  color: var(--color-text-secondary);
}
.dmv-fleet-row__val {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-primary);
}
.dmv-fleet-row__val--warn {
  color: var(--color-warning);
}

/* ── Dot legend ───────────────────────────────────────────────────────────── */
.dmv-legend {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}
.dmv-legend-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
}
.dmv-legend-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* ── Activity list ────────────────────────────────────────────────────────── */
.dmv-activity-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.dmv-activity-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 5px 0;
}
.dmv-activity-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}
.dmv-activity-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.dmv-activity-serial {
  font-family: var(--font-family-mono);
  font-size: var(--font-size-xs);
  color: var(--color-text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dmv-activity-time {
  font-size: 10px;
  color: var(--color-text-muted);
}
.dmv-activity-status {
  font-size: 10px;
  font-weight: var(--font-weight-medium);
  color: var(--color-text-muted);
  white-space: nowrap;
  flex-shrink: 0;
}

/* ── Zoom controls ────────────────────────────────────────────────────────── */
.dmv-zoom-ctrl {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  z-index: 20;
  display: flex;
  flex-direction: column;
  background: rgba(16, 20, 26, 0.82);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-md);
  transition: right 300ms cubic-bezier(0.16, 1, 0.3, 1);
}
.dmv-zoom-ctrl--drawer {
  right: calc(360px + var(--space-4));
}
.dmv-zoom-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: transparent;
  border: none;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}
.dmv-zoom-btn:hover {
  background: var(--color-bg-island);
  color: var(--color-text-primary);
}
.dmv-zoom-divider {
  width: 100%;
  height: 1px;
  background: var(--color-border);
}

/* ── Drawer ───────────────────────────────────────────────────────────────── */
.dmv-drawer {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 360px;
  z-index: 30;
  background: var(--color-bg-void);
  border-left: 1px solid var(--color-border);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 300ms cubic-bezier(0.16, 1, 0.3, 1);
  box-shadow: -12px 0 40px rgba(0,0,0,0.40);
}
.dmv-drawer--open {
  transform: translateX(0);
}

.dmv-drawer__header {
  padding: var(--space-5) var(--space-5) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.dmv-drawer__title-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.dmv-drawer__status-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
}
.dmv-drawer__name {
  flex: 1;
  min-width: 0;
  font-family: var(--font-family-display);
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-primary);
  letter-spacing: var(--tracking-tight);
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dmv-drawer__close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-md);
  border: none;
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
  flex-shrink: 0;
}
.dmv-drawer__close:hover {
  background: var(--color-bg-elevated);
  color: var(--color-text-primary);
}
.dmv-drawer__status-label {
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  padding-left: calc(9px + var(--space-2));
}

.dmv-drawer__body {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-4) var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  scrollbar-width: thin;
  scrollbar-color: var(--color-bg-island) transparent;
}
.dmv-drawer__body::-webkit-scrollbar { width: 4px; }
.dmv-drawer__body::-webkit-scrollbar-track { background: transparent; }
.dmv-drawer__body::-webkit-scrollbar-thumb { background: var(--color-bg-island); border-radius: 4px; }

.dmv-drawer__section {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.dmv-drawer__section-label {
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-muted);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  margin: 0 0 var(--space-1);
}
.dmv-drawer__divider {
  width: 100%;
  height: 1px;
  background: var(--color-border);
}
.dmv-drawer__footer {
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--color-border);
  background: rgba(10, 14, 20, 0.40);
}

/* ── Drawer info row ──────────────────────────────────────────────────────── */
.dmv-drawer-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  min-height: 26px;
}
.dmv-drawer-row__label {
  flex-shrink: 0;
  width: 100px;
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  padding-top: 1px;
}
.dmv-drawer-row__value {
  flex: 1;
  min-width: 0;
  font-size: var(--font-size-sm);
  color: var(--color-text-primary);
  font-weight: var(--font-weight-medium);
  word-break: break-word;
}
.dmv-drawer-row__value--mono {
  font-family: var(--font-family-mono);
  font-size: var(--font-size-xs);
  color: rgba(123, 208, 255, 0.85);
}
.dmv-drawer-row__value--muted {
  color: var(--color-text-muted);
}

/* ── Auth user chips ──────────────────────────────────────────────────────── */
.dmv-user-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.dmv-user-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px 3px 4px;
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
}
.dmv-user-chip__avatar {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--color-primary-subtle);
  border: 1px solid rgba(192, 193, 255, 0.20);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: var(--font-weight-bold);
  color: var(--color-primary);
  line-height: 1;
  flex-shrink: 0;
}
.dmv-user-chip__id {
  font-family: var(--font-family-mono);
  font-size: 10px;
  color: var(--color-text-muted);
  max-width: 120px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.dmv-user-more {
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
  padding: 2px 0;
}

/* ── Issues row ───────────────────────────────────────────────────────────── */
.dmv-issues-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--color-warning-bg);
  border-radius: var(--radius-md);
  border: 1px solid rgba(251, 191, 36, 0.18);
}
.dmv-issues-row__text {
  font-size: var(--font-size-sm);
  color: var(--color-warning);
  font-weight: var(--font-weight-medium);
}

/* ── No coords notice ─────────────────────────────────────────────────────── */
.dmv-no-coords {
  position: absolute;
  bottom: var(--space-4);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: rgba(16, 20, 26, 0.82);
  backdrop-filter: blur(8px);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  pointer-events: none;
  z-index: 15;
}

/* ── Responsive ───────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  .dmv-drawer {
    width: 100%;
    top: auto;
    height: 70%;
    transform: translateY(100%);
    border-left: none;
    border-top: 1px solid var(--color-border);
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
  }
  .dmv-drawer--open {
    transform: translateY(0);
  }
  .dmv-widget--tl {
    top: var(--space-3);
    left: var(--space-3);
  }
  .dmv-widget--bl {
    display: none;
  }
  .dmv-zoom-ctrl {
    top: auto;
    bottom: var(--space-3);
    right: var(--space-3);
    flex-direction: row;
  }
  .dmv-zoom-ctrl--drawer {
    right: var(--space-3);
    bottom: calc(70% + var(--space-3));
  }
  .dmv-zoom-divider {
    width: 1px;
    height: 100%;
  }
}
`
