// frontend/src/pages/bellcloud/devices/DeviceListCardView.jsx
//
// Card-based view of the Device Fleet list.
// Glassmorphism + gradient glow border (online=green, offline=grey)
// + real OSM tile map pulled from device_location_coordinates.
// All colours via design tokens only.

import { useMemo } from 'react'
import Icon from '@/components/ui/Icon'

// ─── Tier config ──────────────────────────────────────────────────────────────

const TIER_META = {
  vip:     { label: 'VIP'     },
  premium: { label: 'Premium' },
  custom:  { label: 'Custom'  },
  small:   { label: 'Small'   },
  mini:    { label: 'Mini'    },
  basic:   { label: 'Basic'   },
}

function getTierLabel(tier) {
  return TIER_META[(tier || 'basic').toLowerCase()]?.label ?? 'Basic'
}

// ─── OSM tile helpers ─────────────────────────────────────────────────────────
// Convert lat/lng to OSM tile x/y at a given zoom level.

// Returns the exact tile coords AND the sub-tile pixel offset (0–255) of the coordinate.
function latLngToTile(lat, lng, zoom) {
  const n = Math.pow(2, zoom)
  const xFrac = ((lng + 180) / 360) * n
  const latRad = (lat * Math.PI) / 180
  const yFrac = ((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2) * n
  const x = Math.floor(xFrac)
  const y = Math.floor(yFrac)
  // px/py: how far into the focal tile the coordinate sits, in pixels (0–256)
  const px = (xFrac - x) * 256
  const py = (yFrac - y) * 256
  return { x, y, px, py }
}

// Build a 4-wide × 3-tall grid of Stamen Toner tiles centred on the coordinate.
// Using 4 columns (dx = -2..+1) guarantees the grid is 1024px wide, which always
// exceeds the card width (~500px) even when the marker sits near the right edge
// of the focal tile (markerOffset.x up to 512px, anchor = 80% - markerOffset.x).
// markerOffset is the pixel position of the coordinate within the 1024×768 grid,
// measured from the grid's top-left. The grid is anchored via inline style so
// markerOffset lands at exactly 80%/60% of the card.
function buildTileGrid(lat, lng, zoom = 13) {
  const { x, y, px, py } = latLngToTile(lat, lng, zoom)
  const tiles = []
  for (let dy = -1; dy <= 1; dy++) {
    for (let dx = -2; dx <= 1; dx++) {
      const tx = x + dx
      const ty = y + dy
      tiles.push({
        url: `https://tiles.stadiamaps.com/tiles/stamen_toner/${zoom}/${tx}/${ty}.png`,
        col: dx + 2 + 1, // cols 1–4
        row: dy + 1 + 1, // rows 1–3
      })
    }
  }
  // The focal tile is at grid column index 2 (dx=0), so its left edge is at
  // 2 × 256 = 512px from the grid's left. markerOffset.x = 512 + px.
  return { tiles, markerOffset: { x: 512 + px, y: 256 + py } }
}

// Normalise GeoPoint from Firestore — can arrive as {lat,lng}, {latitude,longitude},
// or a Firestore GeoPoint object with .latitude / .longitude getters.
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
  return { lat: Number(lat), lng: Number(lng) }
}

// ─── Inline SVGs ─────────────────────────────────────────────────────────────

function BellIcon({ size = 13, color = 'currentColor' }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke={color} strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round"
      aria-hidden="true">
      <path d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9" />
      <path d="M13.73 21a2 2 0 01-3.46 0" />
    </svg>
  )
}

function ClockIcon({ size = 13, color = 'currentColor' }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke={color} strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round"
      aria-hidden="true">
      <circle cx="12" cy="12" r="10" />
      <polyline points="12 6 12 12 16 14" />
    </svg>
  )
}

function AlertTriangleIcon({ size = 13, color = 'currentColor' }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke={color} strokeWidth="1.75" 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>
  )
}


// ─── Real OSM tile map background ────────────────────────────────────────────
// Renders a 3×3 grid of OSM tiles centred on device_location_coordinates,
// tinted dark to match the card theme, with a left-fade gradient overlay.

function MapBackground({ coords }) {
  const ZOOM = 13

  const { tiles, markerOffset } = useMemo(() => {
    if (!coords) return { tiles: null, markerOffset: null }
    return buildTileGrid(coords.lat, coords.lng, ZOOM)
  }, [coords])

  return (
    <div className="dcard__map-bg" aria-hidden="true">
      {tiles ? (
        // 4×3 tile grid. Anchored so markerOffset lands at exactly 80%/60% of
        // the card. The grid is 1024px wide so it always covers the full card.
        <div
          className="dcard__map-tiles"
          style={{
            top:  `calc(60% - ${markerOffset.y}px)`,
            left: `calc(80% - ${markerOffset.x}px)`,
          }}
        >
          {tiles.map((t, i) => (
            <img
              key={i}
              src={t.url}
              alt=""
              className="dcard__map-tile"
              style={{ gridColumn: t.col, gridRow: t.row }}
              draggable={false}
            />
          ))}
        </div>
      ) : (
        <div className="dcard__map-fallback" />
      )}

      {/* Marker pinned at exactly 80%/60% of the card */}
      {tiles && markerOffset && (
        <div aria-hidden="true" style={{
          position: 'absolute',
          top: '60%',
          left: '80%',
          width: 0,
          height: 0,
          zIndex: 10,
          pointerEvents: 'none',
        }}>
          <div style={{
            position: 'absolute',
            width: 9,
            height: 9,
            borderRadius: '50%',
            background: 'rgba(250,204,21,1)',
            boxShadow: '0 0 0 2px rgba(8,12,18,0.95), 0 0 12px rgba(250,204,21,0.8)',
            top: -4.5,
            left: -4.5,
          }} />
          <div style={{
            position: 'absolute',
            width: 24,
            height: 24,
            borderRadius: '50%',
            border: '1.5px solid rgba(250,204,21,0.6)',
            top: -12,
            left: -12,
            animation: 'dcard-marker-pulse 2.2s ease-out infinite',
          }} />
        </div>
      )}

      <div className="dcard__map-tint" />
      <div className="dcard__map-fade" />
    </div>
  )
}

// ─── Online status block (rectangular, spans both identity rows) ──────────────

function OnlineBlock({ online }) {
  return (
    <div
      className={`dcard__online-block ${online ? 'dcard__online-block--on' : 'dcard__online-block--off'}`}
      aria-label={online ? 'Online' : 'Offline'}
    >
      <span className="dcard__online-block-dot" aria-hidden="true" />
      <span className="dcard__online-block-label">{online ? 'Online' : 'Offline'}</span>
    </div>
  )
}

// ─── Inline pill (used for tier, warranty, clock, bells) ─────────────────────

function InlinePill({ icon, label, variant = 'default', title }) {
  return (
    <div className={`dcard__pill dcard__pill--${variant}`} title={title || label}>
      {icon && <span className="dcard__pill-icon" aria-hidden="true">{icon}</span>}
      <span className="dcard__pill-label">{label}</span>
    </div>
  )
}

// ─── Warning count badge ──────────────────────────────────────────────────────

function WarningsBadge({ count }) {
  if (!count) return null
  return (
    <div className="dcard__warn-badge" title={`${count} warning${count > 1 ? 's' : ''} given`}>
      <AlertTriangleIcon size={11} />
      <span>{count}</span>
    </div>
  )
}



// ─── Single Device Card ───────────────────────────────────────────────────────

function DeviceCard({ device, mqttStatusMap, onView, onMiddleClick }) {
  const attr      = device.device_attributes   || {}
  const sub       = device.device_subscription  || {}
  const stats     = device.device_stats          || {}
  const sn        = device.serial_number || device.device_id || ''
  const mqtt      = mqttStatusMap[sn]
  const isOnline  = mqtt ? mqtt.online : device.is_Online

  const name      = device.device_name || 'Unnamed Device'
  const location  = device.device_location
  const coords    = useMemo(() => parseCoords(device.device_location_coordinates), [device.device_location_coordinates])
  const tierLabel     = getTierLabel(sub.subscrTier)
  const hasClock      = !!attr.hasClock
  const hasBells      = !!attr.hasBells
  const totalWarnings = stats.totalWarningsGiven ?? 0


  return (
    <article
      className={`dcard ${isOnline ? 'dcard--online' : 'dcard--offline'}`}
      onClick={onView}
      onMouseDown={(e) => { if (e.button === 1) { e.preventDefault(); onMiddleClick?.(e) } }}
    >
      {/* ── Tier badge — absolute top-right corner ── */}
      <div className="dcard__tier-badge">{tierLabel}</div>

      {/* ── Card content (above map) ── */}
      <div className="dcard__content">
        {/* ── Real OSM map background — fills card, pinned top-left ── */}
        <MapBackground coords={coords} />

        {/* ── Header: status block + identity ── */}
        <header className="dcard__header">
          <OnlineBlock online={isOnline} />

          <div className="dcard__identity">
            <div className="dcard__name-row">
              <span className="dcard__name">{name}</span>
            </div>
            <div className="dcard__serial-row">
              <span className="dcard__serial-hash">#</span>
              <span className="dcard__serial">{sn || '—'}</span>
              {totalWarnings > 0 && <WarningsBadge count={totalWarnings} />}
            </div>
          </div>
        </header>

        {/* ── Feature pills: clock + bells — absolute bottom-left ── */}
        <div className="dcard__pills">
          {hasClock && (
            <InlinePill icon={<ClockIcon size={11} />} label="Clock" variant="feature" title="Has Clock" />
          )}
          {hasBells && (
            <InlinePill icon={<BellIcon size={11} />} label="Bells" variant="feature" title="Has Bells" />
          )}
        </div>

        {/* ── Location label — under marker at 80%/60%, or bottom-right corner ── */}
        {location && (
          <div
            className="dcard__location"
            style={coords
              ? { top: 'calc(60% + 16px)', left: '80%', transform: 'translateX(-50%)' }
              : { bottom: 'var(--space-4)', right: 'var(--space-4)' }
            }
            aria-label={`Location: ${location}`}
          >
            <span className="dcard__location-label">{location}</span>
          </div>
        )}

      </div>
    </article>
  )
}

// ─── Skeleton card ────────────────────────────────────────────────────────────

function SkeletonCard() {
  return (
    <div className="dcard dcard--offline dcard--skeleton" aria-hidden="true">
      <div className="dcard__content">
        <div className="dcard__header">
          <div className="dcard-skel" style={{ width: 56, alignSelf: 'stretch', borderRadius: 8, flexShrink: 0 }} />
          <div className="dcard__identity">
            <div className="dcard__name-row">
              <div className="dcard-skel" style={{ flex: 1, height: 22 }} />
            </div>
            <div className="dcard__serial-row">
              <div className="dcard-skel" style={{ width: '65%', height: 14 }} />
            </div>
          </div>
        </div>
        <div className="dcard__pills">
          {[1, 2, 3].map((i) => (
            <div key={i} className="dcard-skel" style={{ width: 64, height: 26, borderRadius: 999 }} />
          ))}
        </div>
      </div>
    </div>
  )
}

// ─── DeviceListCardView (exported) ────────────────────────────────────────────

export default function DeviceListCardView({
  devices,
  loading,
  mqttStatusMap,
  onView,
  onMiddleClick,
}) {
  if (loading) {
    return (
      <>
        <style>{CARD_STYLES}</style>
        <div className="dcard-grid">
          {Array.from({ length: 9 }).map((_, i) => <SkeletonCard key={i} />)}
        </div>
      </>
    )
  }

  if (!devices.length) {
    return (
      <>
        <style>{CARD_STYLES}</style>
        <div className="dcard-empty">
          <div className="dcard-empty__icon" aria-hidden="true">
            <Icon name="devices" size={36} color="var(--color-text-muted)" />
          </div>
          <p className="dcard-empty__title">No devices found</p>
          <p className="dcard-empty__sub">Try adjusting your search or filters.</p>
        </div>
      </>
    )
  }

  return (
    <>
      <style>{CARD_STYLES}</style>
      <div className="dcard-grid">
        {devices.map((device) => (
          <DeviceCard
            key={device.id}
            device={device}
            mqttStatusMap={mqttStatusMap}
            onView={() => onView(device)}
            onMiddleClick={onMiddleClick ? (e) => onMiddleClick(device, e) : undefined}
          />
        ))}
      </div>
    </>
  )
}

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

const CARD_STYLES = `

/* ── Grid ─────────────────────────────────────────────────────────────────── */
.dcard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
  gap: var(--space-4);
  align-items: flex-start;
}

/* ── Card wrapper — gradient border that changes with online state ────────── */
.dcard {
  position: relative;
  display: flex;
  flex-direction: column;
  border-radius: 16px;
  padding: 1px;        /* the 1px holds the gradient border colour */
  cursor: pointer;
  user-select: none;
  box-sizing: border-box;
  transition: transform 220ms ease, box-shadow 220ms ease, background-image 220ms ease;
  /* min-height ensures border shows on all 4 sides even with short content */
  min-height: 200px;
}

/* Online — green glow border */
.dcard--online {
  background-image: linear-gradient(
    135deg,
    rgba(74,222,128,0.55)  0%,
    rgba(255,255,255,0.08) 40%,
    rgba(255,255,255,0.05) 65%,
    rgba(74,222,128,0.70)  100%
  );
  box-shadow:
    0 8px 32px rgba(0,0,0,0.45),
    0 0 28px rgba(74,222,128,0.10);
}
.dcard--online:hover {
  background-image: linear-gradient(
    135deg,
    rgba(74,222,128,0.80)  0%,
    rgba(255,255,255,0.10) 40%,
    rgba(255,255,255,0.07) 65%,
    rgba(74,222,128,0.95)  100%
  );
  box-shadow:
    0 12px 40px rgba(0,0,0,0.55),
    0 0 48px rgba(74,222,128,0.18);
}

/* Offline — neutral grey border */
.dcard--offline {
  background-image: linear-gradient(
    135deg,
    rgba(120,130,145,0.35) 0%,
    rgba(255,255,255,0.05) 40%,
    rgba(255,255,255,0.03) 65%,
    rgba(90,100,115,0.45)  100%
  );
  box-shadow:
    0 8px 32px rgba(0,0,0,0.45),
    0 0 20px rgba(100,110,125,0.06);
}
.dcard--offline:hover {
  background-image: linear-gradient(
    135deg,
    rgba(140,155,175,0.50) 0%,
    rgba(255,255,255,0.08) 40%,
    rgba(255,255,255,0.05) 65%,
    rgba(110,125,145,0.60) 100%
  );
  box-shadow:
    0 12px 40px rgba(0,0,0,0.55),
    0 0 32px rgba(120,135,155,0.10);
}

.dcard:active { transform: translateY(0) !important; }
.dcard:hover  { transform: translateY(-2px); }

/* ── Card inner surface ──────────────────────────────────────────────────── */
.dcard__content {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  background: rgba(10, 15, 22, 0.92);
  border-radius: 15px;
  padding: var(--space-5) var(--space-5) var(--space-4);
  overflow: hidden;
  /* flex: 1 makes content fill the full card height so all 4 border edges show */
  flex: 1;
  min-height: 210px;
}

/* ── Real OSM tile map background — full card width, fade handles transition ── */
.dcard__map-bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: hidden;
  border-radius: 15px;
}
/* 4×3 tile grid — anchored via inline style so the coordinate lands at 80%/60%.
   1024px wide guarantees full card coverage regardless of sub-tile offset. */
.dcard__map-tiles {
  display: grid;
  grid-template-columns: repeat(4, 256px);
  grid-template-rows: repeat(3, 256px);
  position: absolute;
  width: 1024px;
  height: 768px;
}
.dcard__map-tile {
  width: 256px;
  height: 256px;
  display: block;
  /* Stamen Toner is already B&W — invert so roads are dark lines on dark bg,
     then reduce brightness so it sits quietly behind the card content */
  filter: invert(1) brightness(0.30);
  pointer-events: none;
  user-select: none;
}
/* Fallback grid pattern when no coordinates */
.dcard__map-fallback {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(99,179,237,0.07) 1px, transparent 1px),
    linear-gradient(90deg, rgba(99,179,237,0.07) 1px, transparent 1px);
  background-size: 32px 32px;
}
/* Tint — very light, map is meant to be visible */
.dcard__map-tint {
  position: absolute;
  inset: 0;
  background: rgba(10, 15, 22, 0.05);
}
/* Fade: solid left edge, starts dissolving after 15% */
.dcard__map-fade {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to right,
    rgba(10, 15, 22, 1)    0%,
    rgba(10, 15, 22, 1)    15%,
    rgba(10, 15, 22, 0.60) 40%,
    rgba(10, 15, 22, 0.15) 65%,
    rgba(10, 15, 22, 0)    80%
  );
}
@keyframes dcard-marker-pulse {
  0%   { transform: scale(0.5); opacity: 1; }
  70%  { transform: scale(1.6); opacity: 0; }
  100% { transform: scale(1.6); opacity: 0; }
}


/* ── Header ──────────────────────────────────────────────────────────────── */
.dcard__header {
  display: flex;
  align-items: stretch;
  gap: var(--space-3);
  position: relative;
  z-index: 2;
}
.dcard__identity {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
  justify-content: center;
}
.dcard__name-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}
.dcard__name {
  flex: 1;
  min-width: 0;
  font-family: var(--font-family-display);
  font-size: var(--font-size-xl, 1.25rem);
  font-weight: var(--font-weight-bold);
  color: var(--color-text-primary);
  letter-spacing: -0.01em;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Serial row ──────────────────────────────────────────────────────────── */
.dcard__serial-row {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}
.dcard__serial-hash {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-muted);
  line-height: 1;
  flex-shrink: 0;
}
.dcard__serial {
  font-family: var(--font-family-mono);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: rgba(99, 179, 237, 0.80);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  letter-spacing: 0.04em;
}

/* ── Online status block ─────────────────────────────────────────────────── */
.dcard__online-block {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 58px;
  border-radius: 10px;
  border: 1px solid transparent;
  padding: var(--space-2) var(--space-1);
  transition: background 200ms ease;
}
.dcard__online-block--on {
  background: rgba(74, 222, 128, 0.09);
  border-color: rgba(74, 222, 128, 0.28);
}
.dcard__online-block--off {
  background: rgba(255, 255, 255, 0.03);
  border-color: rgba(255, 255, 255, 0.09);
}
.dcard__online-block-dot {
  display: block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
}
.dcard__online-block--on .dcard__online-block-dot {
  background: #4ade80;
  box-shadow: 0 0 8px rgba(74,222,128,0.75);
  animation: dcard-pulse 2s ease-out infinite;
}
.dcard__online-block--off .dcard__online-block-dot {
  background: rgba(160,170,185,0.40);
}
.dcard__online-block-label {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 1;
  white-space: nowrap;
}
.dcard__online-block--on  .dcard__online-block-label { color: #4ade80; }
.dcard__online-block--off .dcard__online-block-label { color: rgba(160,170,185,0.55); }

@keyframes dcard-pulse {
  0%   { box-shadow: 0 0 0 0   rgba(74, 222, 128, 0.65); }
  70%  { box-shadow: 0 0 0 8px rgba(74, 222, 128, 0);    }
  100% { box-shadow: 0 0 0 0   rgba(74, 222, 128, 0);    }
}

/* ── Warning badge ───────────────────────────────────────────────────────── */
.dcard__warn-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px var(--space-2);
  background: var(--color-warning-bg);
  border: 1px solid rgba(251, 191, 36, 0.20);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  color: var(--color-warning);
  flex-shrink: 0;
}

/* ── Tier badge — absolute top-right of card ─────────────────────────────── */
/* Uses var(--space-4) = 16px to match the bottom-left pills and bottom-right
   location corner — all three shared the same corner inset. */
.dcard__tier-badge {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  z-index: 10;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: rgba(255,255,255,0.07);
  border: 1px solid rgba(255,255,255,0.15);
  color: rgba(200,210,220,0.85);
  white-space: nowrap;
  backdrop-filter: blur(4px);
  pointer-events: none;
}

/* ── Pills row — absolute bottom-left, same inset as tier badge and location corner ── */
.dcard__pills {
  position: absolute;
  bottom: var(--space-4);
  left: var(--space-4);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  z-index: 2;
  pointer-events: none;
}

/* Base pill style */
.dcard__pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 11px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  border: 1px solid transparent;
  white-space: nowrap;
  line-height: 1;
  transition: border-color 150ms ease, background 150ms ease;
}
.dcard__pill-icon {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}
.dcard__pill-label { line-height: 1; }

/* Tier pill — subtle neutral */
.dcard__pill--tier {
  background: rgba(255,255,255,0.05);
  border-color: rgba(255,255,255,0.12);
  color: rgba(200,210,220,0.80);
}

/* Feature pills (clock, bells) — cyan/blue */
.dcard__pill--feature {
  background: rgba(99,179,237,0.07);
  border-color: rgba(99,179,237,0.28);
  color: rgba(99,179,237,0.90);
}
.dcard__pill--feature:hover {
  background: rgba(99,179,237,0.13);
  border-color: rgba(99,179,237,0.50);
}

/* Warranty active — soft green */
.dcard__pill--warranty-on {
  background: rgba(74,222,128,0.07);
  border-color: rgba(74,222,128,0.28);
  color: rgba(74,222,128,0.90);
}

/* No warranty — muted red/amber */
.dcard__pill--warranty-off {
  background: rgba(251,113,133,0.06);
  border-color: rgba(251,113,133,0.22);
  color: rgba(251,113,133,0.80);
}

/* ── Location label ───────────────────────────────────────────────────────── */
/* Position set via inline style in JSX — follows the marker when coords exist,
   falls back to bottom-right corner otherwise. */
.dcard__location {
  position: absolute;
  z-index: 3;
  pointer-events: none;
  max-width: 170px;
}

.dcard__location-label {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(8, 12, 18, 0.72);
  border: 1px solid rgba(250, 204, 21, 0.22);
  backdrop-filter: blur(4px);
  font-size: 12px;
  font-weight: 600;
  color: rgba(250, 204, 21, 0.88);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: 0.02em;
  line-height: 1.4;
}

/* ── Empty state ──────────────────────────────────────────────────────────── */
.dcard-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-16) var(--space-8);
  text-align: center;
  width: 100%;
}
.dcard-empty__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 64px;
  border-radius: var(--radius-xl);
  background: var(--color-bg-elevated);
  margin-bottom: var(--space-2);
}
.dcard-empty__title {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-secondary);
  margin: 0;
}
.dcard-empty__sub {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  margin: 0;
}

/* ── Skeleton shimmer ─────────────────────────────────────────────────────── */
.dcard--skeleton {
  cursor: default;
  pointer-events: none;
}
.dcard-skel {
  background: linear-gradient(
    90deg,
    rgba(255,255,255,0.04) 0%,
    rgba(255,255,255,0.09) 50%,
    rgba(255,255,255,0.04) 100%
  );
  background-size: 200% 100%;
  animation: dcard-shimmer 1.4s ease-in-out infinite;
  border-radius: var(--radius-sm);
}
@keyframes dcard-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ── Responsive ───────────────────────────────────────────────────────────── */
@media (max-width: 768px) {
  .dcard-grid {
    grid-template-columns: 1fr;
    gap: var(--space-3);
  }
}
`
