// Dashboard screen
const GROUPS_KEY            = 'velonics_groups';
const DEVICE_GROUPS_KEY     = 'velonics_device_groups';
const TILE_ORDER_KEY        = 'velonics_tile_order';        // legacy — kept for migration only
const GROUP_TILE_ORDERS_KEY = 'velonics_group_tile_order';  // per-group order map { all:[...], 'g_xxx':[...] }

const DEVICE_COLORS = [
  { key: 'cyan',   hex: '#00d4ff', label: 'Cyan'   },
  { key: 'teal',   hex: '#00b894', label: 'Teal'   },
  { key: 'green',  hex: '#00e676', label: 'Green'  },
  { key: 'amber',  hex: '#ffab40', label: 'Amber'  },
  { key: 'purple', hex: '#a855f7', label: 'Purple' },
  { key: 'indigo', hex: '#6366f1', label: 'Indigo' },
  { key: 'pink',   hex: '#ff4d8d', label: 'Pink'   },
  { key: 'red',    hex: '#ff5252', label: 'Red'    },
];
window.DEVICE_COLORS = DEVICE_COLORS;

const DEVICE_STYLES = [
  { key: 'border', label: 'Border', hint: 'Thin accent strip'  },
  { key: 'bold',   label: 'Bold',   hint: 'Thick accent strip' },
  { key: 'tinted', label: 'Tinted', hint: 'Soft colour wash'   },
  { key: 'glow',   label: 'Glow',   hint: 'Tinted + radiance'  },
];

function _hexToRgb(hex) {
  const r = parseInt(hex.slice(1,3), 16);
  const g = parseInt(hex.slice(3,5), 16);
  const b = parseInt(hex.slice(5,7), 16);
  return `${r},${g},${b}`;
}

// Normalises old string-format ("amber") or new object-format ({color,style}) to object or null
function _normaliseAppearance(val) {
  if (!val) return null;
  if (typeof val === 'string') return { color: val, style: 'border' };
  return val;
}

// Returns Card props: { accent, accentWidth, extraStyle } derived from an appearance object
function _cardStyleProps(appearance) {
  if (!appearance) return { accent: null, accentWidth: 3, extraStyle: {} };
  const colorEntry = DEVICE_COLORS.find(c => c.key === appearance.color);
  if (!colorEntry) return { accent: null, accentWidth: 3, extraStyle: {} };
  const hex = colorEntry.hex;
  const rgb = _hexToRgb(hex);
  switch (appearance.style || 'border') {
    case 'bold':
      return { accent: hex, accentWidth: 6, extraStyle: {} };
    case 'tinted':
      return { accent: hex, accentWidth: 3, extraStyle: { background: `rgba(${rgb},0.10)`, border: `1px solid rgba(${rgb},0.28)` } };
    case 'glow':
      return { accent: hex, accentWidth: 3, extraStyle: { background: `rgba(${rgb},0.12)`, border: `1px solid rgba(${rgb},0.32)`, boxShadow: `0 0 22px rgba(${rgb},0.32), 0 8px 32px rgba(0,0,0,0.28)` } };
    default: // 'border'
      return { accent: hex, accentWidth: 3, extraStyle: {} };
  }
}

function _loadGroups()       { try { return JSON.parse(localStorage.getItem(GROUPS_KEY) || '[]'); } catch { return []; } }
function _loadDeviceGroups() { try { return JSON.parse(localStorage.getItem(DEVICE_GROUPS_KEY) || '{}'); } catch { return {}; } }
function _loadGroupTileOrders() {
  try {
    const stored = JSON.parse(localStorage.getItem(GROUP_TILE_ORDERS_KEY) || '{}');
    if (!stored.all) {
      const old = JSON.parse(localStorage.getItem(TILE_ORDER_KEY) || '[]');
      if (old.length) stored.all = old;
    }
    return stored;
  } catch { return {}; }
}
function _saveGroupTileOrders(o) { try { localStorage.setItem(GROUP_TILE_ORDERS_KEY, JSON.stringify(o)); } catch {} }

const Dashboard = ({ tanks, setTanks, openDetail, setActiveTab, flashed, alertCount, user, issueCmd, pendingCmds, deviceAppearance, onAppearanceChange, onTileOrderChange, cloudTileOrder }) => {
  const [activeGroup,        setActiveGroup]        = useState('all');
  const [groupDrop,          setGroupDrop]          = useState(false);
  const [reorderMode,        setReorderMode]        = useState(false);
  const [groupTileOrders,    setGroupTileOrders]    = useState(_loadGroupTileOrders);
  const [groups,             setGroups]             = useState(_loadGroups);
  const [deviceGroups,       setDeviceGroups]       = useState(_loadDeviceGroups);
  const [appearancePickerTank, setAppearancePickerTank] = useState(null);
  const longPressTimer = useRef(null);

  // Re-read groups/mappings when window regains focus (user navigated from Settings)
  useEffect(() => {
    const sync = () => { setGroups(_loadGroups()); setDeviceGroups(_loadDeviceGroups()); };
    window.addEventListener('focus', sync);
    return () => window.removeEventListener('focus', sync);
  }, []);

  // Apply cloud tile order when it arrives from app.jsx (e.g. fresh device / localStorage cleared)
  useEffect(() => {
    if (!cloudTileOrder) return;
    setGroupTileOrders(cloudTileOrder);
    _saveGroupTileOrders(cloudTileOrder);
  }, [cloudTileOrder]);

  // "All Devices" global order
  const globalOrderedTanks = useMemo(() => {
    const order = groupTileOrders.all || [];
    if (order.length === 0) return tanks;
    const idx = new Map(order.map((id, i) => [String(id), i]));
    return [...tanks].sort((a, b) =>
      (idx.has(String(a.id)) ? idx.get(String(a.id)) : 9999) -
      (idx.has(String(b.id)) ? idx.get(String(b.id)) : 9999)
    );
  }, [tanks, groupTileOrders]);

  // Per-group ordered + filtered list
  const displayTanks = useMemo(() => {
    if (activeGroup === 'all') return globalOrderedTanks;
    const members = globalOrderedTanks.filter(t => (deviceGroups[String(t.id)] || []).includes(activeGroup));
    const groupOrder = groupTileOrders[activeGroup] || [];
    if (groupOrder.length === 0) return members;
    const idx = new Map(groupOrder.map((id, i) => [String(id), i]));
    return [...members].sort((a, b) =>
      (idx.has(String(a.id)) ? idx.get(String(a.id)) : 9999) -
      (idx.has(String(b.id)) ? idx.get(String(b.id)) : 9999)
    );
  }, [globalOrderedTanks, activeGroup, deviceGroups, groupTileOrders]);

  // Move within the current view's order (independent per group)
  const moveTile = (localIdx, dir) => {
    const ids = displayTanks.map(t => String(t.id));
    const to = localIdx + dir;
    if (to < 0 || to >= ids.length) return;
    [ids[localIdx], ids[to]] = [ids[to], ids[localIdx]];
    setGroupTileOrders(prev => {
      const next = { ...prev, [activeGroup]: ids };
      _saveGroupTileOrders(next);
      onTileOrderChange?.(next);
      return next;
    });
  };

  const now = new Date();
  const greeting = now.getHours() < 12 ? 'Good Morning' : now.getHours() < 17 ? 'Good Afternoon' : 'Good Evening';
  const dateStr = now.toLocaleDateString('en-IN', { weekday: 'long', day: 'numeric', month: 'short' });

  const handleMotorToggle = (tank) => {
    if (!tank.deviceId || tank.virtual) {
      if (tank.type === 'Agri Pump') {
        const nextRunning = !isPumpRunning(tank.motorStatus);
        setTanks(prev => prev.map(t =>
          t.id === tank.id
            ? { ...t, motor: nextRunning, motorStatus: nextRunning ? 'running_manual' : 'stopped_undervolt' }
            : t
        ));
      } else {
        setTanks(prev => prev.map(t => t.id === tank.id ? { ...t, motor: !t.motor } : t));
      }
      return;
    }
    if (!tank.motor) {
      issueCmd(tank.id, 'motor',     () => MQ.publishCmd(tank.deviceId, 'motor', true));
      issueCmd(tank.id, 'motorMode', () => MQ.publishCmd(tank.deviceId, 'mode', 'manual'));
    } else {
      issueCmd(tank.id, 'motor', () => MQ.publishCmd(tank.deviceId, 'motor', false));
    }
  };
  const handleValveToggle = (tank) => {
    const next = !tank.valveIn;
    if (tank.deviceId) issueCmd(tank.id, 'valve', () => MQ.publishValveCmd(tank.deviceId, next, tank.valveOut));
  };
  const handleModeToggle = (tank, mode) => {
    if (tank.deviceId) issueCmd(tank.id, 'motorMode', () => MQ.publishCmd(tank.deviceId, 'mode', mode));
  };

  return (
    <div style={{ padding: '8px 16px 100px' }} onClick={() => groupDrop && setGroupDrop(false)}>
      {/* Greeting */}
      <div style={{ padding: '6px 4px 14px' }}>
        <div style={{ fontSize: 22, fontWeight: 600, lineHeight: 1.2 }}>{greeting}, <span style={{
          background: 'linear-gradient(90deg, var(--cyan), var(--teal))',
          WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', backgroundClip: 'text',
        }}>{user ? user.name.split(' ')[0] : 'there'}</span></div>
        <div style={{ fontSize: 13, color: 'var(--text-2)', marginTop: 4 }}>
          {dateStr} · {tanks.filter(t => t.online).length} of {tanks.length} devices online
        </div>
      </div>

      {/* Group filter tabs */}
      <div style={{ position: 'relative', marginBottom: 14 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, overflowX: 'auto', paddingBottom: 2, scrollbarWidth: 'none', msOverflowStyle: 'none' }}>
          {/* All Groups pill — opens group dropdown */}
          <button onClick={e => { e.stopPropagation(); setGroupDrop(v => !v); }} style={{
            flexShrink: 0, display: 'flex', alignItems: 'center', gap: 5,
            padding: '6px 14px', borderRadius: 20, fontSize: 12, fontWeight: 500,
            background: groupDrop ? 'rgba(0,212,255,0.12)' : 'rgba(255,255,255,0.04)',
            border: `1px solid ${groupDrop ? 'rgba(0,212,255,0.4)' : 'var(--border)'}`,
            color: groupDrop ? 'var(--cyan)' : 'var(--text-2)', cursor: 'pointer',
            whiteSpace: 'nowrap',
          }}>
            All Groups
            <Ic name="chevronDown" size={12} color={groupDrop ? 'var(--cyan)' : 'var(--text-3)'}
              style={{ transition: 'transform 200ms', transform: groupDrop ? 'rotate(180deg)' : 'rotate(0deg)' }}/>
          </button>
          <GroupTab label="All Devices" active={activeGroup === 'all'} onClick={() => setActiveGroup('all')}/>
          {groups.map(g => (
            <GroupTab key={g.id} label={g.name} active={activeGroup === g.id} onClick={() => setActiveGroup(g.id)}/>
          ))}
        </div>
        {groupDrop && (
          <div style={{
            position: 'absolute', top: '110%', left: 0, zIndex: 100,
            background: 'var(--bg-2)', border: '1px solid var(--border)',
            borderRadius: 14, padding: '6px 0', minWidth: 170,
            boxShadow: '0 8px 28px rgba(0,0,0,0.5)',
          }} onClick={e => e.stopPropagation()}>
            {[{ id: 'all', name: 'All Devices' }, ...groups].map(g => (
              <div key={g.id} onClick={() => { setActiveGroup(g.id); setGroupDrop(false); }} style={{
                padding: '10px 16px', fontSize: 13,
                fontWeight: activeGroup === g.id ? 600 : 400,
                color: activeGroup === g.id ? 'var(--cyan)' : 'var(--text)',
                cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 8,
              }}>
                {activeGroup === g.id
                  ? <Ic name="check" size={13} color="var(--cyan)"/>
                  : <span style={{ width: 13, display: 'inline-block' }}/>
                }
                {g.name}
              </div>
            ))}
            {groups.length === 0 && (
              <div style={{ padding: '10px 16px', fontSize: 12, color: 'var(--text-3)' }}>No groups yet · create in Settings</div>
            )}
          </div>
        )}
      </div>

      {/* Section header with reorder toggle */}
      <SectionTitle action={
        <button onClick={() => setReorderMode(v => !v)} style={{
          background: 'transparent', border: 'none',
          color: reorderMode ? 'var(--cyan)' : 'var(--text-3)',
          fontSize: 12, fontWeight: 500,
          display: 'inline-flex', alignItems: 'center', gap: 4, cursor: 'pointer',
        }}>
          <Ic name="edit" size={12}/> {reorderMode ? 'Done' : 'Reorder'}
        </button>
      }>Your Devices</SectionTitle>

      {/* Device tiles */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        {displayTanks.map((t, localIdx) => {
          const tileAppearance = _normaliseAppearance(deviceAppearance?.[String(t.id)]);
          return (
            <div key={t.id}
              onTouchStart={() => { clearTimeout(longPressTimer.current); longPressTimer.current = setTimeout(() => setAppearancePickerTank(t), 520); }}
              onTouchEnd={() => clearTimeout(longPressTimer.current)}
              onTouchMove={() => clearTimeout(longPressTimer.current)}
              onContextMenu={e => { e.preventDefault(); setAppearancePickerTank(t); }}
            >
              {reorderMode && (
                <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4, padding: '0 2px' }}>
                  <span style={{ flex: 1, fontSize: 11, color: 'var(--text-3)', paddingLeft: 4 }}>{t.name}</span>
                  <button onClick={e => { e.stopPropagation(); moveTile(localIdx, -1); }} disabled={localIdx === 0} style={{
                    width: 28, height: 24, borderRadius: 7, border: '1px solid var(--border)',
                    background: 'rgba(255,255,255,0.06)', color: localIdx === 0 ? 'var(--text-3)' : 'var(--text)',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    cursor: localIdx === 0 ? 'default' : 'pointer',
                  }}><Ic name="chevUp" size={13}/></button>
                  <button onClick={e => { e.stopPropagation(); moveTile(localIdx, 1); }} disabled={localIdx >= displayTanks.length - 1} style={{
                    width: 28, height: 24, borderRadius: 7, border: '1px solid var(--border)',
                    background: 'rgba(255,255,255,0.06)', color: localIdx >= displayTanks.length - 1 ? 'var(--text-3)' : 'var(--text)',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    cursor: localIdx >= displayTanks.length - 1 ? 'default' : 'pointer',
                  }}><Ic name="chevronDown" size={13}/></button>
                </div>
              )}
              {t.type === 'Smart Plug'
                ? <PlugCard plug={t} flash={flashed.has(t.id)} tileAppearance={tileAppearance}
                    onTap={() => openDetail(t.id)}
                    onPowerToggle={() => {
                      if (t.on) {
                        setTanks(prev => prev.map(x => x.id === t.id ? { ...x, on: false, current: 0, power: 0 } : x));
                        if (t.deviceId && !t.virtual && window.MQ) MQ.publishCmd(t.deviceId, 'power', false);
                      } else {
                        setTanks(prev => prev.map(x => x.id === t.id ? { ...x, on: true } : x));
                        if (t.deviceId && !t.virtual && window.MQ) MQ.publishCmd(t.deviceId, 'power', true);
                      }
                    }}/>
                : t.type === 'Smart AC'
                ? <AcCard ac={t} flash={flashed.has(t.id)} tileAppearance={tileAppearance}
                    onTap={() => openDetail(t.id)}
                    onPowerToggle={() => {
                      if (t.on) {
                        setTanks(prev => prev.map(x => x.id === t.id ? { ...x, on: false, power: 0, current: 0 } : x));
                        if (t.deviceId && !t.virtual && window.MQ) MQ.publishCmd(t.deviceId, 'power', false);
                      } else {
                        setTanks(prev => prev.map(x => x.id === t.id ? { ...x, on: true } : x));
                        if (t.deviceId && !t.virtual && window.MQ) MQ.publishCmd(t.deviceId, 'power', true);
                      }
                    }}/>
                : t.type === 'Smart RO'
                ? <ROCard ro={t} flash={flashed.has(t.id)} onTap={() => openDetail(t.id)} tileAppearance={tileAppearance}/>
                : t.type === 'Agri Pump'
                ? <AgriPumpCard tank={t} flash={flashed.has(t.id)} pending={pendingCmds[t.id] || {}} tileAppearance={tileAppearance}
                    onTap={() => openDetail(t.id)} onMotorToggle={() => handleMotorToggle(t)}/>
                : t.type === 'Energy Meter'
                ? <EnergyMeterCard tank={t} flash={flashed.has(t.id)} onTap={() => openDetail(t.id)} tileAppearance={tileAppearance}/>
                : t.type === 'Occupancy Sensor'
                ? <OccupancyCard tank={t} flash={flashed.has(t.id)} onTap={() => openDetail(t.id)} tileAppearance={tileAppearance}/>
                : <TankCard tank={t} flash={flashed.has(t.id)} pending={pendingCmds[t.id] || {}} tileAppearance={tileAppearance}
                    onTap={() => openDetail(t.id)} onMotorToggle={() => handleMotorToggle(t)}
                    onValveToggle={() => handleValveToggle(t)} onModeToggle={mode => handleModeToggle(t, mode)}/>
              }
            </div>
          );
        })}
      </div>

      <_AppearanceSheet
        tank={appearancePickerTank}
        deviceAppearance={deviceAppearance}
        onSelect={(appearance) => {
          if (!appearancePickerTank) return;
          const next = appearance
            ? { ...deviceAppearance, [String(appearancePickerTank.id)]: appearance }
            : Object.fromEntries(Object.entries(deviceAppearance || {}).filter(([k]) => k !== String(appearancePickerTank.id)));
          onAppearanceChange?.(next);
        }}
        onClose={() => setAppearancePickerTank(null)}
      />

    </div>
  );
};

const GroupTab = ({ label, active, onClick }) => (
  <button onClick={onClick} style={{
    flexShrink: 0, padding: '6px 14px', borderRadius: 20, fontSize: 12,
    fontWeight: active ? 600 : 500,
    background: active ? 'linear-gradient(90deg, var(--cyan), var(--teal))' : 'rgba(255,255,255,0.04)',
    border: active ? '1px solid transparent' : '1px solid var(--border)',
    color: active ? '#0a0e27' : 'var(--text-2)', cursor: 'pointer',
    transition: 'all 150ms ease', whiteSpace: 'nowrap',
  }}>{label}</button>
);

const TankCard = ({ tank, onTap, onMotorToggle, onValveToggle, onModeToggle, flash, pending = {}, tileAppearance }) => {
  const levelTone = tank.level > 50 ? 'green' : tank.level >= 25 ? 'amber' : 'red';
  const levelColor = levelTone === 'green' ? 'var(--green)' : levelTone === 'amber' ? 'var(--amber)' : 'var(--red)';
  const offline = !tank.online;
  const waitingForLive = tank.online && !tank.mqttReceived;
  const { accent: tileAccent, accentWidth, extraStyle = {} } = _cardStyleProps(tileAppearance);

  return (
    <Card accent={tileAccent || (levelTone === 'red' ? 'red' : 'cyan')} accentWidth={accentWidth} style={{ padding: 14, opacity: offline ? 0.75 : 1, ...extraStyle }}>
      <div onClick={onTap} style={{ cursor: 'pointer' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 10 }}>
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <div style={{ width: 28, height: 28, borderRadius: 8, background: 'rgba(0,212,255,0.12)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <Ic name={tank.type === 'Underground' ? 'site' : tank.type === 'Irrigation' ? 'sprout' : 'home'} size={14} color="var(--cyan)"/>
              </div>
              <div style={{ fontWeight: 600, fontSize: 15, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{tank.name}</div>
            </div>
            <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 4, marginLeft: 36 }}>{tank.location} · {tank.capacity}L</div>
            {tank.sharedBy && (
              <div style={{ fontSize: 10, color: 'var(--text-3)', marginLeft: 36, marginTop: 2 }}>
                Shared by {tank.sharedBy.name || tank.sharedBy.email}
              </div>
            )}
          </div>
          <div style={{display:'flex', gap:5, alignItems:'center'}}>
            {tank.virtual && <Badge color="amber" size="sm">Virtual</Badge>}
            <Badge color={!tank.deviceId ? 'gray' : offline ? 'red' : waitingForLive ? 'gray' : 'green'} size="sm">
              <StatusDot kind={!tank.deviceId ? 'gray' : offline ? 'red' : waitingForLive ? 'gray' : 'green'} size={6} pulse={!offline && !waitingForLive && !!tank.deviceId}/>
              {!tank.deviceId ? 'No Device' : offline ? 'Offline' : waitingForLive ? 'Waiting…' : 'Online'}
            </Badge>
          </div>
        </div>

        <div style={{ display: 'flex', gap: 14, marginTop: 14, alignItems: 'center' }}>
          {tank.type === 'Underground'
            ? <UndergroundTankVisual level={tank.level} online={tank.online} motor={tank.motor && tank.online} valveOpen={tank.valveIn && tank.online} width={88} height={120}/>
            : <TankVisual level={tank.level} online={tank.online} motor={tank.motor && tank.online} valveOpen={tank.valveIn && tank.online} width={88} height={120}/>
          }
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
              <span className="num" style={{ fontSize: 38, fontWeight: 700, color: levelColor, lineHeight: 1, letterSpacing: -1 }}>{Math.round(tank.level)}</span>
              <span style={{ fontSize: 16, color: 'var(--text-2)', fontWeight: 500 }}>%</span>
            </div>
            <div className="num" style={{ fontSize: 12, color: 'var(--text-2)', marginTop: 4 }}>
              {AC.fmtNum(Math.round(tank.capacity * tank.level / 100))} <span style={{ color: 'var(--text-3)' }}>/ {AC.fmtNum(tank.capacity)} L</span>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginTop: 12 }}>
              <MiniRow icon="power" label="Motor" status={tank.motor ? 'Running' : 'Stopped'} color={tank.motor ? 'var(--green)' : 'var(--text-2)'} pulse={tank.motor}/>
              <MiniRow icon="clock" label="Last run" status={`${Math.floor(tank.runtime / 60)}h ${Math.round(tank.runtime) % 60}m`} color="var(--text-2)" plain/>
            </div>
          </div>
        </div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: 8, marginTop: 14 }}>
        <ControlBtn label={tank.motor ? 'Motor ON' : 'Motor OFF'} icon="power" active={tank.motor} onClick={onMotorToggle} color="green" disabled={offline || waitingForLive} pending={pending.motor}/>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 10, paddingTop: 10, borderTop: '1px solid var(--border)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, color: 'var(--text-2)' }}>
          Mode {pending.motorMode && <PendingDots size="sm" color="var(--cyan)"/>}
        </div>
        <Segmented size="sm" options={[{ value:'auto', label:'Auto' }, { value:'manual', label:'Manual' }]} value={tank.motorMode} onChange={onModeToggle} disabled={offline || waitingForLive || !!pending.motorMode}/>
      </div>
    </Card>
  );
};

const MiniRow = ({ icon, label, status, color, pulse, plain }) => (
  <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12 }}>
    <Ic name={icon} size={12} color={plain ? 'var(--text-3)' : color}/>
    <span style={{ color: 'var(--text-3)' }}>{label}</span>
    <span style={{ marginLeft: 'auto', color, fontWeight: 500, display: 'inline-flex', alignItems: 'center', gap: 4 }}>
      {pulse && <StatusDot kind="green" size={6} pulse/>}{status}
    </span>
  </div>
);

/* ------------------------------------------------------------------ */
// Connectivity status: Online / Waiting / Offline / Demo
const _plugConnStatus = (plug) => {
  if (!plug.deviceId) return { label: 'Demo',    color: 'gray',  dot: 'gray',  pulse: false };
  if (plug.online)    return { label: 'Online',   color: 'green', dot: 'green', pulse: true  };
  const neverSeen = !plug.lastSeen || plug.lastSeen === '—';
  if (neverSeen)      return { label: 'Waiting',  color: 'amber', dot: 'amber', pulse: false };
  return               { label: 'Offline',  color: 'red',   dot: 'red',   pulse: false };
};

// Heroic switch — large, centered, visually dominant, style varies by kind
// isOn, offline, kind, onClick, pending are all explicit props
const _HeroSwitch = ({ isOn, offline, kind = 'round', onClick, pending = false }) => {
  const green   = 'var(--green)';
  const red     = 'var(--red)';
  const color   = isOn ? green : red;
  const handleClick = (e) => { e.stopPropagation(); if (!offline && !pending) onClick(); };

  if (kind === 'toggle') {
    return (
      <div style={{ display:'flex', justifyContent:'center', alignItems:'center', gap:20, padding:'16px 0 8px' }}>
        <div style={{ fontSize:14, fontWeight:700, color: isOn ? 'var(--text-3)' : red }}>OFF</div>
        <button onClick={handleClick} style={{
          position:'relative', width:44, height:88, borderRadius:22,
          background: isOn ? 'rgba(0,230,118,0.18)' : 'rgba(255,82,82,0.12)',
          border: `2px solid ${isOn ? green : red}`,
          cursor: offline ? 'not-allowed' : 'pointer',
          opacity: offline ? 0.5 : 1,
          transition:'all 250ms ease',
        }}>
          <div style={{
            position:'absolute', left:6, right:6, height:36, borderRadius:16,
            background: isOn ? green : red,
            top: isOn ? 6 : 46,
            transition:'top 250ms ease, background 250ms ease',
            boxShadow: isOn ? '0 0 16px rgba(0,230,118,0.55)' : '0 0 16px rgba(255,82,82,0.45)',
          }}/>
        </button>
        <div style={{ fontSize:14, fontWeight:700, color: isOn ? green : 'var(--text-3)' }}>ON</div>
      </div>
    );
  }

  if (kind === 'rocker') {
    return (
      <div onClick={handleClick} style={{
        width:'100%',
        cursor: offline ? 'not-allowed' : 'pointer',
        opacity: offline ? 0.5 : 1,
        margin:'14px 0 8px',
        display:'flex', borderRadius:18, overflow:'hidden',
        border: `2px solid ${isOn ? 'rgba(0,230,118,0.45)' : 'rgba(255,82,82,0.45)'}`,
        height:80, transition:'border-color 250ms',
      }}>
        <div style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', gap:6, background: isOn ? 'rgba(0,230,118,0.18)' : 'rgba(255,255,255,0.03)', transition:'all 250ms' }}>
          <Ic name="power" size={22} color={isOn ? green : 'var(--text-3)'}/>
          <span style={{ fontSize:14, fontWeight:700, color: isOn ? green : 'var(--text-3)' }}>ON</span>
        </div>
        <div style={{ width:2, background: isOn ? 'rgba(0,230,118,0.3)' : 'rgba(255,82,82,0.3)' }}/>
        <div style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', gap:6, background: isOn ? 'rgba(255,255,255,0.02)' : 'rgba(255,82,82,0.12)', transition:'all 250ms' }}>
          <Ic name="power" size={22} color={isOn ? 'var(--text-3)' : red}/>
          <span style={{ fontSize:14, fontWeight:700, color: isOn ? 'var(--text-3)' : red }}>OFF</span>
        </div>
      </div>
    );
  }

  if (kind === 'push') {
    return (
      <button onClick={handleClick} style={{
        cursor: offline ? 'not-allowed' : 'pointer',
        opacity: offline ? 0.5 : 1,
        margin:'14px 0 8px', width:'100%',
        padding:'18px', borderRadius:18,
        background: isOn ? 'rgba(0,230,118,0.12)' : 'rgba(255,82,82,0.08)',
        border: `2px solid ${isOn ? 'rgba(0,230,118,0.45)' : 'rgba(255,82,82,0.45)'}`,
        display:'flex', flexDirection:'column', alignItems:'center', gap:8,
        transition:'all 250ms ease',
      }}>
        <div style={{ width:56, height:56, borderRadius:'50%', background: isOn ? green : red, display:'flex', alignItems:'center', justifyContent:'center', boxShadow: isOn ? '0 0 24px rgba(0,230,118,0.55)' : '0 0 24px rgba(255,82,82,0.45)', transition:'all 250ms ease' }}>
          <Ic name="power" size={26} color={isOn ? '#062b18' : '#fff'}/>
        </div>
        <span style={{ fontSize:14, fontWeight:700, color, letterSpacing:1, textTransform:'uppercase' }}>{isOn ? 'ON' : 'OFF'}</span>
      </button>
    );
  }

  // Default: large round power button (hero)
  return (
    <div style={{ display:'flex', flexDirection:'column', alignItems:'center', padding:'16px 0 8px' }}>
      <button onClick={handleClick} style={{
        position:'relative', width:92, height:92, borderRadius:'50%',
        background: isOn ? 'radial-gradient(circle at 30% 25%, #6dffb0, #00b860 70%)' : 'radial-gradient(circle at 30% 25%, #ff8a80, #ff1744 70%)',
        border: `2px solid ${isOn ? 'rgba(0,230,118,0.55)' : 'rgba(255,82,82,0.55)'}`,
        boxShadow: isOn ? '0 0 36px rgba(0,230,118,0.5)' : '0 0 36px rgba(255,82,82,0.4)',
        cursor: offline ? 'not-allowed' : 'pointer',
        opacity: offline ? 0.5 : 1,
        transition:'all 350ms cubic-bezier(.4,1.4,.6,1)',
        animation: isOn ? 'pulse-glow 2.4s ease-in-out infinite' : undefined,
        display:'flex', alignItems:'center', justifyContent:'center',
      }}>
        {isOn && <div style={{ position:'absolute', inset:-12, border:'2px dashed rgba(255,255,255,0.3)', borderRadius:'50%', animation:'spin 9s linear infinite' }}/>}
        <Ic name="power" size={40} color={isOn ? '#062b18' : '#fff'} strokeWidth={2.2}/>
      </button>
      <div style={{ marginTop:8, fontSize:13, fontWeight:700, color, letterSpacing:1 }}>{isOn ? 'ON' : 'OFF'}</div>
    </div>
  );
};
window._HeroSwitch = _HeroSwitch;

const PlugCard = ({ plug, onTap, onPowerToggle, flash, tileAppearance }) => {
  const isOn    = plug.virtual ? plug.on : (plug.on && plug.online);
  const offline = !plug.virtual && !plug.online && !!plug.deviceId;
  const accentColor = isOn ? 'var(--green)' : 'var(--text-3)';
  const activeSchedCount = (plug.schedules || []).filter(s => s.enabled).length;
  const conn = _plugConnStatus(plug);
  const { accent: tileAccent, accentWidth, extraStyle = {} } = _cardStyleProps(tileAppearance);
  return (
    <Card accent={tileAccent || (isOn ? 'green' : 'cyan')} accentWidth={accentWidth} onClick={onTap} style={{ padding: 14, cursor: 'pointer', opacity: (!plug.virtual && !plug.online && plug.deviceId) ? 0.82 : 1,
      animation: flash ? 'flash-glow 600ms ease both' : undefined, ...extraStyle }}>

      {/* Header — name, location, badge */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 10 }}>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <div style={{ width: 28, height: 28, borderRadius: 8, background: isOn ? 'rgba(0,230,118,0.15)' : 'rgba(255,255,255,0.06)', display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'background 300ms' }}>
              <Ic name="plug" size={14} color={isOn ? 'var(--green)' : 'var(--text-2)'}/>
            </div>
            <div style={{ fontWeight: 600, fontSize: 15, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{plug.name}</div>
          </div>
          <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 4, marginLeft: 36 }}>
            {plug.location}{plug.appliance ? ` · ${plug.appliance}` : ''}
          </div>
          {plug.sharedBy && (
            <div style={{ fontSize: 10, color: 'var(--text-3)', marginLeft: 36, marginTop: 2 }}>
              Shared by {plug.sharedBy.name || plug.sharedBy.email}
            </div>
          )}
        </div>
        <div style={{display:'flex', gap:5, alignItems:'center'}}>
          {plug.virtual && <Badge color="amber" size="sm">Virtual</Badge>}
          <Badge color={conn.color} size="sm">
            <StatusDot kind={conn.dot} size={6} pulse={conn.pulse}/>
            {conn.label}
          </Badge>
        </div>
      </div>

      {/* Heroic switch — the visual centrepiece */}
      <_HeroSwitch isOn={isOn} offline={offline} kind={plug.switchType || 'round'} onClick={onPowerToggle}/>

      {/* Footer — wattage + energy info */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingTop: 8, borderTop: '1px solid var(--border)' }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 4 }}>
          <span className="num" style={{ fontSize: 20, fontWeight: 700, color: accentColor, lineHeight: 1, letterSpacing: -0.5, transition: 'color 300ms' }}>
            {isOn ? AC.fmtNum(plug.power) : 0}
          </span>
          <span style={{ fontSize: 11, color: 'var(--text-2)', fontWeight: 500 }}>W</span>
          {isOn && <span style={{ fontSize: 10, color: 'var(--text-3)', marginLeft: 4 }}>{(plug.current||0).toFixed(1)}A</span>}
        </div>
        <div style={{ fontSize: 11, color: 'var(--text-3)', textAlign: 'right' }}>
          {(plug.energyToday || 0).toFixed(2)} kWh
          {activeSchedCount > 0 && <span style={{ color: 'var(--cyan)', marginLeft: 6 }}>{activeSchedCount} sched</span>}
          {!activeSchedCount && plug.timer && <span style={{ color: 'var(--amber)', marginLeft: 6 }}>timer</span>}
        </div>
      </div>
    </Card>
  );
};
/* ------------------------------------------------------------------ */

const _AC_MODE_MAP = {
  cool: { label: 'Cool', icon: 'snowflake', color: 'var(--cyan)'  },
  fan:  { label: 'Fan',  icon: 'wind',      color: 'var(--teal)'  },
  dry:  { label: 'Dry',  icon: 'droplet',   color: 'var(--info)'  },
  heat: { label: 'Heat', icon: 'flame',     color: 'var(--amber)' },
};

const AcCard = ({ ac, onTap, onPowerToggle, flash, tileAppearance }) => {
  const _isVirtual = !ac.deviceId || !!ac.virtual;
  const isOn  = _isVirtual ? ac.on : (ac.on && ac.online);
  const offline = !_isVirtual && !ac.online && !!ac.deviceId;
  const conn = !ac.deviceId ? { label: 'Demo',    color: 'gray',  dot: 'gray',  pulse: false }
             : ac.online    ? { label: 'Online',   color: 'green', dot: 'green', pulse: true  }
             : (!ac.lastSeen || ac.lastSeen === '—') ? { label: 'Waiting', color: 'amber', dot: 'amber', pulse: false }
             : { label: 'Offline', color: 'red', dot: 'red', pulse: false };
  const modeEntry = _AC_MODE_MAP[ac.mode] || _AC_MODE_MAP.cool;
  const { accent: tileAccent, accentWidth, extraStyle = {} } = _cardStyleProps(tileAppearance);
  return (
    <Card accent={tileAccent || (isOn ? 'cyan' : 'default')} accentWidth={accentWidth}
      onClick={onTap} style={{ padding: 14, cursor: 'pointer',
        opacity: (!_isVirtual && !ac.online && ac.deviceId) ? 0.82 : 1,
        animation: flash ? 'flash-glow 600ms ease both' : undefined, ...extraStyle }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 10 }}>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <div style={{ width: 28, height: 28, borderRadius: 8,
              background: isOn ? 'rgba(0,188,212,0.15)' : 'rgba(255,255,255,0.06)',
              display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'background 300ms' }}>
              <Ic name="thermometer" size={14} color={isOn ? 'var(--cyan)' : 'var(--text-2)'}/>
            </div>
            <div style={{ fontWeight: 600, fontSize: 15, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{ac.name}</div>
          </div>
          <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 4, marginLeft: 36 }}>{ac.location}</div>
        </div>
        <div style={{ display: 'flex', gap: 5, alignItems: 'center' }}>
          {ac.virtual && <Badge color="amber" size="sm">Virtual</Badge>}
          <Badge color={conn.color} size="sm">
            <StatusDot kind={conn.dot} size={6} pulse={conn.pulse}/>
            {conn.label}
          </Badge>
        </div>
      </div>
      <_HeroSwitch isOn={isOn} offline={offline} kind={ac.switchType || 'round'} onClick={onPowerToggle}/>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', paddingTop: 8, borderTop: '1px solid var(--border)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <span className="num" style={{ fontSize: 20, fontWeight: 700, color: isOn ? 'var(--cyan)' : 'var(--text-3)', lineHeight: 1 }}>{ac.temp || 24}°C</span>
          <span style={{ fontSize: 11, color: modeEntry.color, fontWeight: 600 }}>{modeEntry.label}</span>
        </div>
        <div style={{ fontSize: 11, color: 'var(--text-3)' }}>
          {isOn ? `${AC.fmtNum(ac.power || 0)} W` : 'Off'}
          {ac.fanSpeed && <span style={{ color: 'var(--text-3)', marginLeft: 6 }}>{ac.fanSpeed}</span>}
        </div>
      </div>
    </Card>
  );
};

/* ------------------------------------------------------------------ */

const ROCard = ({ ro, onTap, flash, tileAppearance }) => {
  const motorOn  = !!ro.motor && !!ro.online;
  const tds      = ro.outletTDS ?? ro.tds ?? 0;
  const tdsColor = tds > 100 ? 'var(--red)' : tds > 50 ? 'var(--amber)' : 'var(--green)';
  const rejection = (ro.saltRejection || 0).toFixed(1);
  const { accent: tileAccent, accentWidth, extraStyle = {} } = _cardStyleProps(tileAppearance);
  return (
    <Card accent={tileAccent || (motorOn ? 'cyan' : 'default')} accentWidth={accentWidth} style={{ padding: 14,
      opacity: (!ro.online && ro.deviceId) ? 0.82 : 1,
      animation: flash ? 'flash-glow 600ms ease both' : undefined, ...extraStyle }}>
      <div onClick={onTap} style={{ cursor: 'pointer' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 10 }}>
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <div style={{ width: 28, height: 28, borderRadius: 8,
                background: motorOn ? 'rgba(0,188,212,0.15)' : 'rgba(255,255,255,0.06)',
                display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'background 300ms' }}>
                <Ic name="droplets" size={14} color={motorOn ? 'var(--cyan)' : 'var(--text-2)'}/>
              </div>
              <div style={{ fontWeight: 600, fontSize: 15, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                {ro.name}
              </div>
            </div>
            <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 4, marginLeft: 36 }}>
              {ro.location}
            </div>
            {ro.sharedBy && (
              <div style={{ fontSize: 10, color: 'var(--text-3)', marginTop: 2, marginLeft: 36 }}>
                Shared by {ro.sharedBy.name || ro.sharedBy.email}
              </div>
            )}
          </div>
          <div style={{display:'flex', gap:5, alignItems:'center'}}>
            {ro.virtual && <Badge color="amber" size="sm">Virtual</Badge>}
            <Badge color={!ro.deviceId ? 'gray' : !ro.online ? 'red' : !ro.mqttReceived ? 'gray' : 'green'} size="sm">
              <StatusDot kind={!ro.deviceId ? 'gray' : !ro.online ? 'red' : !ro.mqttReceived ? 'gray' : 'green'} size={6} pulse={ro.online && !!ro.mqttReceived}/>
              {!ro.deviceId ? 'Demo' : !ro.online ? 'Offline' : !ro.mqttReceived ? 'Waiting…' : 'Online'}
            </Badge>
          </div>
        </div>

        <div style={{ marginTop: 14 }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginBottom: 8 }}>
            <span className="num" style={{ fontSize: 34, fontWeight: 700, color: tdsColor, lineHeight: 1, letterSpacing: -1 }}>
              {Math.round(tds)}
            </span>
            <span style={{ fontSize: 14, color: 'var(--text-2)', fontWeight: 500 }}>ppm TDS</span>
            <span style={{ fontSize: 11, color: 'var(--text-3)', marginLeft: 4 }}>outlet</span>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
            <MiniRow icon="gauge"    label="Rejection"    status={`${rejection}%`}      color="var(--cyan)" plain/>
            <MiniRow icon="droplets" label="Purified today" status={`${(ro.purifiedToday || 0).toFixed(1)} L`} color="var(--text-2)" plain/>
          </div>
        </div>
      </div>
    </Card>
  );
};
/* ------------------------------------------------------------------ */

const ControlBtn = ({ label, icon, active, onClick, color, disabled, pending }) => {
  const c = color === 'green' ? 'var(--green)' : 'var(--cyan)';
  const blocked = disabled || pending;
  return (
    <button onClick={e => { e.stopPropagation(); !blocked && onClick(e); }} disabled={blocked} style={{
      display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8, padding: '11px 12px',
      background: pending ? 'rgba(255,255,255,0.04)' : active ? `${c}20` : 'rgba(255,255,255,0.04)',
      border: `1px solid ${pending ? 'rgba(255,255,255,0.12)' : active ? `${c}55` : 'var(--border)'}`,
      color: pending ? 'var(--text-3)' : active ? c : 'var(--text-2)',
      borderRadius: 12, fontWeight: 600, fontSize: 12, letterSpacing: 0.4,
      opacity: blocked ? 0.7 : 1, cursor: blocked ? 'not-allowed' : 'pointer', transition: 'all 200ms ease',
    }}>
      {pending ? <PendingDots size="sm" color={c}/> : <><Ic name={icon} size={14}/>{label}</>}
    </button>
  );
};

const MOTOR_STATUS_LABEL = {
  waiting_3phase:      'Waiting — 3 Phase',
  running_auto:        'Running — Auto',
  running_manual:      'Running — Manual',
  stopped_overvolt:    'Stopped — Over Voltage',
  stopped_undervolt:   'Stopped — Under Voltage',
  stopped_imbalance:   'Stopped — Phase Imbalance',
  stopped_overpressure:'Stopped — Over Pressure',
  stopped_dryrun:      'Stopped — Dry Run',
};

const MOTOR_STATUS_COLOR = {
  waiting_3phase:      'var(--amber)',
  running_auto:        'var(--green)',
  running_manual:      'var(--cyan)',
  stopped_overvolt:    'var(--red)',
  stopped_undervolt:   'var(--red)',
  stopped_imbalance:   'var(--red)',
  stopped_overpressure:'var(--red)',
  stopped_dryrun:      'var(--red)',
};

const isPumpRunning = (s) => s === 'running_auto' || s === 'running_manual';
const isPowerHealthy = (tank) => !tank.motorStatus || isPumpRunning(tank.motorStatus) || tank.motorStatus === 'waiting_3phase';

const _agriSwitchType = (id) => { try { return (JSON.parse(localStorage.getItem('velonics_agri_data') || '{}')[id] || {}).switchType || 'round'; } catch { return 'round'; } };

const AgriPumpCard = ({ tank, onTap, onMotorToggle, flash, pending = {}, tileAppearance }) => {
  const offline = !tank.online;
  const waitingForLive = tank.online && !tank.mqttReceived;
  const running = isPumpRunning(tank.motorStatus);
  const healthy = isPowerHealthy(tank);
  const statusLabel = MOTOR_STATUS_LABEL[tank.motorStatus] || '—';
  const statusColor = MOTOR_STATUS_COLOR[tank.motorStatus] || 'var(--text-2)';
  const controlsReady = !offline && !waitingForLive;
  const { accent: tileAccent, accentWidth, extraStyle = {} } = _cardStyleProps(tileAppearance);

  const schedRow = () => {
    if (!tank.schedulerMode || tank.schedulerMode === 'off') {
      const pct = tank.runtimeSet > 0 ? Math.round(((tank.runtimeSet - tank.runtimeRemaining) / tank.runtimeSet) * 100) : 0;
      return (
        <div style={{ marginTop: 10 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--text-3)' }}>Runtime · {tank.runtimeRemaining ?? '—'} min left</span>
            <span className="num" style={{ fontSize: 11, color: 'var(--text-2)' }}>{tank.runtimeCurrent ?? '—'} / {tank.runtimeSet ?? '—'} min</span>
          </div>
          <div style={{ height: 4, borderRadius: 999, background: 'rgba(255,255,255,0.08)', overflow: 'hidden' }}>
            <div style={{ width: `${pct}%`, height: '100%', background: 'linear-gradient(90deg, var(--cyan), var(--teal))', borderRadius: 999, transition: 'width 1s ease' }}/>
          </div>
        </div>
      );
    }
    // CYCLIC
    const cycleOn = tank.currentCycle === 'on';
    const rem = cycleOn ? tank.runtimeRemaining : tank.offtimeRemaining;
    const total = cycleOn ? tank.runtimeSet : tank.offtimeSet;
    const pct = total > 0 ? Math.round(((total - rem) / total) * 100) : 0;
    return (
      <div style={{ marginTop: 10 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }}>
          <span style={{ fontSize: 11, color: 'var(--text-3)' }}>
            Cycle: <span style={{ color: cycleOn ? 'var(--green)' : 'var(--amber)', fontWeight: 600 }}>{cycleOn ? 'ON' : 'OFF'}</span>
            {'  ·  '}{rem ?? '—'} min left
          </span>
          <span className="num" style={{ fontSize: 11, color: 'var(--text-2)' }}>
            {cycleOn ? `RT: ${tank.runtimeSet ?? '—'} min` : `OT: ${tank.offtimeSet ?? '—'} min`}
          </span>
        </div>
        <div style={{ height: 4, borderRadius: 999, background: 'rgba(255,255,255,0.08)', overflow: 'hidden' }}>
          <div style={{ width: `${pct}%`, height: '100%', background: cycleOn ? 'linear-gradient(90deg, var(--green), var(--teal))' : 'linear-gradient(90deg, var(--amber), #ff7043)', borderRadius: 999, transition: 'width 1s ease' }}/>
        </div>
      </div>
    );
  };

  const switchType = tank.switchType || _agriSwitchType(tank.id);

  return (
    <Card accent={tileAccent || (running ? 'cyan' : 'red')} accentWidth={accentWidth} onClick={onTap} style={{ padding: 14, cursor: 'pointer', opacity: offline ? 0.75 : 1, animation: flash ? 'flash-glow 1s ease-out' : undefined, ...extraStyle }}>
      {/* Header */}
      <div>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 10 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, flex: 1 }}>
            <div style={{
              width: 28, height: 28, borderRadius: 8, flexShrink: 0,
              background: healthy ? 'rgba(0,230,118,0.14)' : 'rgba(255,82,82,0.14)',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>
              <div style={{ width: 10, height: 10, borderRadius: '50%', background: healthy ? 'var(--green)' : 'var(--red)', boxShadow: healthy ? '0 0 8px var(--green)' : '0 0 8px var(--red)', animation: running ? 'pulse-dot 2s infinite' : undefined }}/>
            </div>
            <div>
              <div style={{ fontWeight: 600, fontSize: 15, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{tank.name}</div>
              {tank.sharedBy && <div style={{ fontSize: 10, color: 'var(--text-3)', marginTop: 1 }}>Shared by {tank.sharedBy.name || tank.sharedBy.email}</div>}
            </div>
          </div>
          <div style={{display:'flex', gap:5, alignItems:'center'}}>
            {tank.virtual && <Badge color="amber" size="sm">Virtual</Badge>}
            <Badge color={!tank.deviceId ? 'gray' : offline ? 'red' : waitingForLive ? 'gray' : 'green'} size="sm">
              <StatusDot kind={!tank.deviceId ? 'gray' : offline ? 'red' : waitingForLive ? 'gray' : 'green'} size={6} pulse={!offline && !waitingForLive && !!tank.deviceId}/>
              {!tank.deviceId ? 'No Device' : offline ? 'Offline' : waitingForLive ? 'Waiting…' : 'Online'}
            </Badge>
          </div>
        </div>

        <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 4, marginLeft: 36 }}>{tank.location}</div>

        {/* Motor status label */}
        <div style={{ marginTop: 8, display: 'flex', alignItems: 'center', gap: 6 }}>
          <StatusDot kind={running ? 'green' : tank.motorStatus === 'waiting_3phase' ? 'amber' : 'red'} size={7} pulse={running}/>
          <span style={{ fontSize: 12, fontWeight: 600, color: statusColor }}>{statusLabel}</span>
        </div>

        {/* Scheduler progress bar */}
        {tank.schedulerMode && schedRow()}
      </div>

      {/* Heroic motor switch */}
      <_HeroSwitch isOn={running} offline={!tank.virtual && !controlsReady} kind={switchType} onClick={onMotorToggle} pending={pending.motor}/>
    </Card>
  );
};

const EnergyMeterCard = ({ tank, onTap, flash, tileAppearance }) => {
  const offline = !tank.online;
  const waitingForLive = tank.online && !tank.mqttReceived;
  const hasLoad = tank.power > 0;
  const powerKw = tank.power >= 1000 ? `${(tank.power / 1000).toFixed(2)} kW` : `${Math.round(tank.power || 0)} W`;
  const { accent: tileAccent, accentWidth, extraStyle = {} } = _cardStyleProps(tileAppearance);

  return (
    <Card accent={tileAccent || (offline ? 'red' : hasLoad ? 'cyan' : 'gray')} accentWidth={accentWidth} style={{ padding: 14, opacity: offline ? 0.75 : 1, animation: flash ? 'flash-glow 1s ease-out' : undefined, ...extraStyle }} onClick={onTap}>
      <div style={{ cursor: 'pointer' }}>
        {/* Header */}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 10 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, flex: 1 }}>
            <div style={{ width: 28, height: 28, borderRadius: 8, background: 'rgba(255,171,64,0.14)', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <Ic name="bolt" size={14} color="var(--amber)"/>
            </div>
            <div>
              <div style={{ fontWeight: 600, fontSize: 15, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{tank.name}</div>
              {tank.sharedBy && <div style={{ fontSize: 10, color: 'var(--text-3)', marginTop: 1 }}>Shared by {tank.sharedBy.name || tank.sharedBy.email}</div>}
            </div>
          </div>
          <div style={{display:'flex', gap:5, alignItems:'center'}}>
            {tank.virtual && <Badge color="amber" size="sm">Virtual</Badge>}
            <Badge color={!tank.deviceId ? 'gray' : offline ? 'red' : waitingForLive ? 'gray' : 'green'} size="sm">
              <StatusDot kind={!tank.deviceId ? 'gray' : offline ? 'red' : waitingForLive ? 'gray' : 'green'} size={6} pulse={!offline && !waitingForLive && !!tank.deviceId}/>
              {!tank.deviceId ? 'No Device' : offline ? 'Offline' : waitingForLive ? 'Waiting…' : 'Online'}
            </Badge>
          </div>
        </div>
        <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 4, marginLeft: 36 }}>{tank.location}</div>

        {/* Load status */}
        <div style={{ marginTop: 10, display: 'flex', alignItems: 'center', gap: 6 }}>
          <StatusDot kind={hasLoad ? 'green' : 'gray'} size={7} pulse={hasLoad}/>
          <span style={{ fontSize: 13, fontWeight: 600, color: hasLoad ? 'var(--green)' : 'var(--text-2)' }}>
            {offline ? 'Offline' : hasLoad ? 'Load Active' : 'No Load'}
          </span>
        </div>

        {/* Voltage / Current / Power */}
        <div style={{ marginTop: 10, display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 6 }}>
          {[
            { label: 'Voltage', val: tank.voltage ? `${Math.round(tank.voltage)} V` : '— V', color: 'var(--cyan)' },
            { label: 'Current', val: tank.current ? `${tank.current.toFixed(1)} A` : '— A', color: 'var(--info)' },
            { label: 'Power',   val: powerKw,                                                  color: 'var(--amber)' },
          ].map(({ label, val, color }) => (
            <div key={label} style={{ background: 'rgba(255,255,255,0.04)', borderRadius: 10, padding: '8px 6px', textAlign: 'center' }}>
              <div style={{ fontSize: 9, color: 'var(--text-3)', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: 4 }}>{label}</div>
              <div className="num" style={{ fontSize: 14, fontWeight: 700, color }}>{val}</div>
            </div>
          ))}
        </div>

        {/* Energy today */}
        <div style={{ marginTop: 8, display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 10px', background: 'rgba(255,171,64,0.06)', border: '1px solid rgba(255,171,64,0.18)', borderRadius: 10 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <Ic name="zap" size={12} color="var(--amber)"/>
            <span style={{ fontSize: 12, color: 'var(--text-2)' }}>Energy today</span>
          </div>
          <span className="num" style={{ fontSize: 14, fontWeight: 700, color: 'var(--amber)' }}>
            {tank.energyToday != null ? tank.energyToday.toFixed(3) : '—'} <span style={{ fontSize: 10, fontWeight: 400, color: 'var(--text-3)' }}>kWh</span>
          </span>
        </div>
      </div>
    </Card>
  );
};

const OccupancyCard = ({ tank, onTap, flash, tileAppearance }) => {
  const offline       = !tank.online;
  const waitingForLive = tank.online && !tank.mqttReceived;
  const occupied      = !!tank.occupied;
  const zone          = tank.osZone || 'Room';

  const fmtSecs = (s) => {
    if (!s || s < 1) return '—';
    if (s >= 3600) return `${Math.floor(s / 3600)}h ${Math.floor((s % 3600) / 60)}m`;
    if (s >= 60)   return `${Math.floor(s / 60)}m ${s % 60}s`;
    return `${s}s`;
  };

  const { accent: tileAccent, accentWidth, extraStyle = {} } = _cardStyleProps(tileAppearance);
  const statusColor = offline ? 'red' : occupied ? 'green' : 'gray';

  return (
    <Card accent={tileAccent || statusColor} accentWidth={accentWidth}
      style={{ padding: 14, opacity: offline ? 0.75 : 1, animation: flash ? 'flash-glow 1s ease-out' : undefined, cursor: 'pointer', ...extraStyle }}
      onClick={onTap}>
      {/* Header */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 10 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, flex: 1 }}>
          <div style={{ width: 28, height: 28, borderRadius: 8, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
            background: occupied ? 'rgba(0,230,118,0.14)' : 'rgba(255,255,255,0.06)' }}>
            <Ic name={occupied ? 'user' : 'userX'} size={14} color={occupied ? 'var(--green)' : 'var(--text-3)'}/>
          </div>
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ fontWeight: 600, fontSize: 15, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{tank.name}</div>
            {tank.sharedBy && <div style={{ fontSize: 10, color: 'var(--text-3)', marginTop: 1 }}>Shared by {tank.sharedBy.name || tank.sharedBy.email}</div>}
          </div>
        </div>
        <div style={{display:'flex', gap:5, alignItems:'center'}}>
          {tank.virtual && <Badge color="amber" size="sm">Virtual</Badge>}
          <Badge color={!tank.deviceId ? 'gray' : offline ? 'red' : waitingForLive ? 'gray' : 'green'} size="sm">
            <StatusDot kind={!tank.deviceId ? 'gray' : offline ? 'red' : waitingForLive ? 'gray' : 'green'} size={6} pulse={!offline && !waitingForLive && !!tank.deviceId}/>
            {!tank.deviceId ? 'No Device' : offline ? 'Offline' : waitingForLive ? 'Waiting…' : 'Online'}
          </Badge>
        </div>
      </div>
      <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 4, marginLeft: 36 }}>{tank.location} · {zone}</div>

      {/* Occupancy status */}
      <div style={{ marginTop: 10, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <div style={{
            width: 10, height: 10, borderRadius: '50%',
            background: occupied ? 'var(--green)' : 'var(--text-3)',
            boxShadow: occupied ? '0 0 8px var(--green)' : 'none',
            animation: occupied ? 'pulse-dot 2s infinite' : 'none',
            flexShrink: 0,
          }}/>
          <span style={{ fontSize: 14, fontWeight: 700, color: occupied ? 'var(--green)' : 'var(--text-2)' }}>
            {offline ? 'Offline' : occupied ? 'OCCUPIED' : 'VACANT'}
          </span>
        </div>
        {tank.relayOn != null && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 11, color: tank.relayOn ? 'var(--cyan)' : 'var(--text-3)' }}>
            <StatusDot kind={tank.relayOn ? 'green' : 'gray'} size={5} pulse={!!tank.relayOn}/>
            Relay {tank.relayOn ? 'ON' : 'OFF'}
          </div>
        )}
      </div>

      {/* Stat row */}
      <div style={{ marginTop: 10, display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 6 }}>
        {[
          { label: 'Duration',   val: fmtSecs(tank.duration || 0),       color: 'var(--text)' },
          { label: 'Events',     val: `${tank.countToday ?? 0}`,          color: 'var(--cyan)'  },
          { label: 'Today',      val: fmtSecs(tank.runtimeToday || 0),   color: 'var(--teal)' },
        ].map(({ label, val, color }) => (
          <div key={label} style={{ background: 'rgba(255,255,255,0.04)', borderRadius: 10, padding: '8px 6px', textAlign: 'center' }}>
            <div style={{ fontSize: 9, color: 'var(--text-3)', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: 4 }}>{label}</div>
            <div className="num" style={{ fontSize: 13, fontWeight: 700, color }}>{val}</div>
          </div>
        ))}
      </div>
    </Card>
  );
};

// Mini card preview used inside _AppearanceSheet to illustrate each style option
const _StylePreview = ({ styleKey, colorHex, selected }) => {
  const rgb = colorHex ? _hexToRgb(colorHex) : '0,212,255';
  const hex = colorHex || '#00d4ff';
  const wrapStyle = {
    width: '100%', height: 46, borderRadius: 10, position: 'relative', overflow: 'hidden',
    background:
      styleKey === 'tinted' ? `rgba(${rgb},0.13)` :
      styleKey === 'glow'   ? `rgba(${rgb},0.16)` :
      'rgba(255,255,255,0.06)',
    border:
      styleKey === 'tinted' ? `1px solid rgba(${rgb},0.32)` :
      styleKey === 'glow'   ? `1px solid rgba(${rgb},0.40)` :
      selected ? `1.5px solid ${hex}` : '1px solid rgba(255,255,255,0.1)',
    boxShadow:
      styleKey === 'glow' ? `0 0 14px rgba(${rgb},0.4)` : 'none',
    transition: 'all 150ms ease',
  };
  const accentW = styleKey === 'bold' ? 5 : 3;
  return (
    <div style={wrapStyle}>
      <div style={{
        position: 'absolute', left: 0, top: 0, bottom: 0, width: accentW,
        background: `linear-gradient(180deg, ${hex}, transparent)`,
        borderRadius: '10px 0 0 10px',
      }}/>
      <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', paddingLeft: accentW + 8, gap: 6 }}>
        <div style={{ width: 8, height: 8, borderRadius: '50%', background: hex, opacity: 0.9 }}/>
        <div style={{ height: 4, width: 32, borderRadius: 99, background: 'rgba(255,255,255,0.2)' }}/>
      </div>
    </div>
  );
};

const _AppearanceSheet = ({ tank, deviceAppearance, onSelect, onClose }) => {
  const current = _normaliseAppearance(deviceAppearance?.[String(tank?.id)]);
  const [draftColor, setDraftColor] = useState(current?.color || null);
  const [draftStyle, setDraftStyle] = useState(current?.style || 'border');
  if (!tank) return null;

  const colorHex = DEVICE_COLORS.find(c => c.key === draftColor)?.hex || null;
  const hasAppearance = !!current;

  const apply = () => {
    if (draftColor) onSelect({ color: draftColor, style: draftStyle });
    onClose();
  };
  const remove = () => { onSelect(null); onClose(); };

  return ReactDOM.createPortal(
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, zIndex: 200,
      background: 'rgba(0,0,0,0.55)',
      display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', alignItems: 'center',
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        width: '100%', maxWidth: 460,
        background: 'var(--bg-2)', borderRadius: '20px 20px 0 0',
        borderTop: '1px solid var(--border-strong)',
        padding: '20px 20px',
        paddingBottom: 'calc(20px + env(safe-area-inset-bottom))',
        animation: 'slide-up 220ms ease both',
      }}>
        {/* Header */}
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 18 }}>
          <button onClick={onClose} style={{ background: 'none', border: 'none', padding: '4px 0', color: 'var(--text-3)', fontSize: 14, cursor: 'pointer' }}>
            Cancel
          </button>
          <div style={{ textAlign: 'center' }}>
            <div style={{ fontSize: 15, fontWeight: 600 }}>Tile Appearance</div>
            <div style={{ fontSize: 11, color: 'var(--text-3)', marginTop: 1 }}>{tank.name}</div>
          </div>
          <div style={{ width: 48 }}/>
        </div>

        {/* Style selector */}
        <div style={{ fontSize: 10, fontWeight: 600, letterSpacing: 1, color: 'var(--text-3)', marginBottom: 10 }}>STYLE</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8, marginBottom: 20 }}>
          {DEVICE_STYLES.map(s => {
            const sel = draftStyle === s.key;
            return (
              <button key={s.key} onClick={() => { setDraftStyle(s.key); if (!draftColor) setDraftColor('cyan'); }} style={{
                background: sel ? 'rgba(255,255,255,0.07)' : 'transparent',
                border: `1.5px solid ${sel ? (colorHex || 'var(--cyan)') : 'rgba(255,255,255,0.08)'}`,
                borderRadius: 12, padding: '8px 4px 6px', cursor: 'pointer',
                display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6,
                transition: 'all 150ms ease',
              }}>
                <_StylePreview styleKey={s.key} colorHex={colorHex} selected={sel}/>
                <span style={{ fontSize: 10, color: sel ? 'var(--text)' : 'var(--text-3)', fontWeight: sel ? 600 : 400 }}>{s.label}</span>
              </button>
            );
          })}
        </div>

        {/* Colour picker */}
        <div style={{ fontSize: 10, fontWeight: 600, letterSpacing: 1, color: 'var(--text-3)', marginBottom: 10 }}>COLOUR</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10, marginBottom: 20 }}>
          {DEVICE_COLORS.map(c => {
            const sel = draftColor === c.key;
            return (
              <button key={c.key} onClick={() => setDraftColor(c.key)} style={{
                display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6,
                padding: '10px 4px', borderRadius: 14,
                background: sel ? 'rgba(255,255,255,0.07)' : 'transparent',
                border: `1.5px solid ${sel ? c.hex : 'transparent'}`,
                cursor: 'pointer', transition: 'all 150ms ease',
              }}>
                <div style={{
                  width: 38, height: 38, borderRadius: 11, background: c.hex,
                  boxShadow: sel ? `0 0 14px ${c.hex}90` : 'none',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  transition: 'all 150ms ease',
                }}>
                  {sel && <Ic name="check" size={17} color="#0a0e27" strokeWidth={2.5}/>}
                </div>
                <span style={{ fontSize: 10, color: sel ? 'var(--text)' : 'var(--text-3)', fontWeight: sel ? 600 : 400 }}>{c.label}</span>
              </button>
            );
          })}
        </div>

        {/* Primary action */}
        <button onClick={apply} disabled={!draftColor} style={{
          width: '100%', padding: '13px', borderRadius: 14, marginBottom: 10,
          background: draftColor ? 'linear-gradient(90deg, var(--cyan), var(--teal))' : 'rgba(255,255,255,0.06)',
          border: 'none',
          color: draftColor ? '#0a0e27' : 'var(--text-3)',
          fontSize: 15, fontWeight: 700, cursor: draftColor ? 'pointer' : 'default',
          display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7,
          transition: 'all 200ms ease',
        }}>
          <Ic name="check" size={16} color={draftColor ? '#0a0e27' : 'var(--text-3)'} strokeWidth={2.5}/>
          Set Appearance
        </button>

        {/* Remove — only shown when appearance already exists */}
        {hasAppearance && (
          <button onClick={remove} style={{
            width: '100%', padding: '10px', borderRadius: 12,
            background: 'transparent', border: 'none',
            color: 'var(--text-3)', fontSize: 13, cursor: 'pointer',
          }}>
            Remove custom appearance
          </button>
        )}
      </div>
    </div>,
    document.getElementById('modal-root')
  );
};

window.Dashboard = Dashboard;
