// screens.jsx — Tempo, Gráficos, Câmera and Estação tabs

const { useState, useEffect, useMemo, useRef } = React;

const ACCENT = 'oklch(0.78 0.14 75)';
const COOL = 'oklch(0.78 0.10 200)';

const fmt = (v, d = 1) => (v == null || !Number.isFinite(v) ? '—' : v.toFixed(d));

function fmtAge(sec) {
  if (sec == null) return '—';
  if (sec < 60) return `${Math.round(sec)}s`;
  if (sec < 3600) return `${Math.round(sec / 60)}min`;
  if (sec < 86400 * 2) return `${Math.round(sec / 3600)}h`;
  return `${Math.round(sec / 86400)}d`;
}

function localTime(ts, withDay = false) {
  if (!ts) return '—';
  return new Date(ts * 1000).toLocaleString('pt-BR', withDay
    ? { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }
    : { hour: '2-digit', minute: '2-digit' });
}

// ── Reusable bits ──────────────────────────────────────────────────────────

function MetricCard({ label, value, unit, trend, trendDecimals = 2, sparkData, sparkKey, color, dim, large = false, sub, children }) {
  return (
    <div className={`metric-card ${large ? 'large' : ''}`}>
      <div className="metric-head">
        <span className="metric-label">{label}</span>
        {trend != null && Number.isFinite(trend) && (
          <span className={`metric-trend ${trend > 0 ? 'up' : trend < 0 ? 'down' : ''}`}>
            {trend > 0 ? '▲' : trend < 0 ? '▼' : '·'} {Math.abs(trend).toFixed(trendDecimals)}
          </span>
        )}
      </div>
      <div className="metric-row">
        <div className="metric-value-wrap">
          <span className="metric-value mono" style={{ color }}>{value}</span>
          <span className="metric-unit">{unit}</span>
        </div>
        {children}
        {!children && sparkData && sparkData.length > 1 && (
          <div className="metric-spark">
            <Sparkline data={sparkData} valueKey={sparkKey} color={color} dim={dim} />
          </div>
        )}
      </div>
      {sub && <div className="metric-sub">{sub}</div>}
    </div>
  );
}

// Row closest to `secondsAgo` before the latest one (rows are oldest first).
function rowAgo(history, latest, secondsAgo) {
  const target = latest.ts - secondsAgo;
  let best = null;
  for (const r of history) {
    if (r.ts > target + 450) break;
    best = r;
  }
  return best && Math.abs(best.ts - target) < 1800 ? best : null;
}

function diff(a, b, key) {
  return a && b && a[key] != null && b[key] != null ? a[key] - b[key] : null;
}

function rangeOver(history, key, hours, nowSec) {
  const cutoff = nowSec - hours * 3600;
  const vals = history.filter(d => d.ts >= cutoff && d[key] != null && Number.isFinite(d[key])).map(d => d[key]);
  return vals.length ? { min: Math.min(...vals), max: Math.max(...vals) } : { min: null, max: null };
}

function Compass({ deg, color, dim }) {
  const size = 58;
  const c = size / 2;
  const has = deg != null && Number.isFinite(deg);
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} className="compass">
      <circle cx={c} cy={c} r={c - 3} fill="none" stroke={dim} strokeWidth="1" />
      {Array.from({ length: 16 }).map((_, i) => {
        const a = (i * 22.5 - 90) * Math.PI / 180;
        const r1 = c - 3, r2 = i % 4 === 0 ? c - 9 : c - 6;
        return <line key={i} x1={c + r1 * Math.cos(a)} y1={c + r1 * Math.sin(a)} x2={c + r2 * Math.cos(a)} y2={c + r2 * Math.sin(a)} stroke={dim} strokeWidth="1" />;
      })}
      <text x={c} y={11} textAnchor="middle" fontSize="7" fill={dim} fontFamily="JetBrains Mono">N</text>
      {has && (
        // Arrow points where the wind blows TO; deg is where it comes FROM
        <g transform={`rotate(${deg + 180} ${c} ${c})`}>
          <line x1={c} y1={c + 14} x2={c} y2={c - 14} stroke={color} strokeWidth="2" strokeLinecap="round" />
          <path d={`M ${c - 5} ${c - 8} L ${c} ${c - 16} L ${c + 5} ${c - 8} Z`} fill={color} />
        </g>
      )}
    </svg>
  );
}

// ── Tempo tab ──────────────────────────────────────────────────────────────

function WeatherTab({ history, latest, dark, deviceId, demo, lastPhoto, nowSec, onOpenCamera }) {
  if (!latest) {
    return (
      <div className="tab-content">
        <div className="empty-state">
          <span className="empty-eyebrow mono">SEM DADOS</span>
          <p>A estação <span className="mono">{deviceId}</span> ainda não enviou nenhuma leitura.</p>
        </div>
      </div>
    );
  }

  const dim = dark ? 'rgba(255,255,255,0.18)' : 'rgba(0,0,0,0.18)';
  const fg = dark ? '#e8eaed' : '#1a1d21';
  const ref = latest.ts;

  const oneHourAgo = rowAgo(history, latest, 3600);
  const threeHrAgo = rowAgo(history, latest, 3 * 3600);
  const pressureChange = diff(latest, threeHrAgo, 'pressure');
  const tempRange24 = rangeOver(history, 'temperature', 24, ref);
  const last6h = history.filter(d => d.ts >= ref - 6 * 3600);
  const gust1h = rangeOver(history, 'wind_gust_kmh', 1, ref).max;
  const rain1h = window.sumOver(history, 'rain_mm', 1, ref);
  const rainDay = window.rainToday(history, ref);
  const rain24 = window.sumOver(history, 'rain_mm', 24, ref);
  const lux = latest.lux;
  const probPct = latest.rain_probability != null ? Math.round(latest.rain_probability * 100) : null;
  const stormy = (latest.rain_probability ?? 0) >= window.RAIN_THRESHOLD;
  const seasonKey = window.seasonForDate(new Date(ref * 1000));

  return (
    <div className="tab-content">
      {/* Hero — last sky photo */}
      <section className="photo-hero" onClick={lastPhoto ? onOpenCamera : undefined}>
        {lastPhoto ? (
          <>
            <img src={window.AS_API.photoUrl(deviceId, lastPhoto.path)} alt="Última foto do céu" />
            <div className="photo-hero-cap">
              <span className="mono">CÂMERA · {localTime(lastPhoto.ts || lastPhoto.uploaded_at, true)}</span>
              <span className="mono">ver todas ›</span>
            </div>
          </>
        ) : (
          <div className="photo-hero-empty">
            <span className="hero-eyebrow">Câmera · OV2640 160°</span>
            <span className="mono">{demo ? 'sem fotos na demonstração' : 'nenhuma foto recebida ainda'}</span>
          </div>
        )}
      </section>

      {/* Sensor grid */}
      <section className="sensor-grid">
        <MetricCard
          label="TEMPERATURA" value={fmt(latest.temperature, 1)} unit="°C"
          trend={diff(latest, oneHourAgo, 'temperature')} trendDecimals={1}
          sparkData={last6h} sparkKey="temperature" color={fg} dim={dim} large
          sub={tempRange24.min != null
            ? `mín ${tempRange24.min.toFixed(1)}° · máx ${tempRange24.max.toFixed(1)}° · 24h · SHT41`
            : null}
        />
        <MetricCard
          label="UMIDADE" value={fmt(latest.humidity, 0)} unit="%"
          trend={diff(latest, oneHourAgo, 'humidity')} trendDecimals={1}
          sparkData={last6h} sparkKey="humidity" color={fg} dim={dim}
        />
        <MetricCard
          label="PRESSÃO" value={fmt(latest.pressure, 1)} unit="hPa"
          trend={pressureChange}
          sparkData={last6h} sparkKey="pressure"
          color={pressureChange != null && pressureChange < -1 ? ACCENT : fg} dim={dim}
          sub="na estação · sem redução"
        />
        <MetricCard
          label="VENTO" value={fmt(latest.wind_speed_kmh, 1)} unit="km/h" color={fg} dim={dim}
          sub={`rajada ${fmt(latest.wind_gust_kmh, 0)} · máx 1h ${fmt(gust1h, 0)}`}
        >
          <div className="compass-wrap">
            <Compass deg={latest.wind_dir_deg} color={COOL} dim={dim} />
            <span className="mono compass-lbl">{window.compassName(latest.wind_dir_deg)}</span>
          </div>
        </MetricCard>
        <MetricCard
          label="CHUVA HOJE" value={fmt(rainDay ?? 0, 1)} unit="mm"
          color={rain1h ? COOL : fg} dim={dim}
          sparkData={last6h} sparkKey="rain_mm"
          sub={`1h ${fmt(rain1h ?? 0, 1)} · 24h ${fmt(rain24 ?? 0, 1)} mm`}
        />
        <MetricCard
          label="NEBULOSIDADE" value={fmt(latest.cloud_cover, 0)} unit="%"
          trend={diff(latest, oneHourAgo, 'cloud_cover')} trendDecimals={0}
          sparkData={last6h} sparkKey="cloud_cover" color={fg} dim={dim}
          sub={latest.sky_temperature != null ? `IR céu ${latest.sky_temperature.toFixed(1)}°C` : null}
        />
        <MetricCard
          label="LUZ" value={lux == null ? '—' : lux >= 10000 ? (lux / 1000).toFixed(0) : lux >= 1000 ? (lux / 1000).toFixed(1) : lux.toFixed(0)}
          unit={lux != null && lux >= 1000 ? 'klx' : 'lx'}
          sparkData={last6h} sparkKey="lux" color={fg} dim={dim}
          sub="BH1750"
        />
      </section>

      {/* Rain model (reference) */}
      <section className="hero">
        <div className="hero-head">
          <div className="hero-titles">
            <span className="hero-eyebrow">Modelo de chuva · próx. 3 h · referência</span>
            <h2 className="hero-title">
              {probPct == null ? 'Modelo sem dados' : (stormy ? 'Chuva provável' : 'Sem chuva prevista')}
            </h2>
          </div>
          <div className={`hero-prob ${stormy ? 'storm' : 'clear'}`}>
            <span className="hero-prob-num mono">{probPct == null ? '—' : probPct}</span>
            <span className="hero-prob-pct">%</span>
          </div>
        </div>
        <RainTimeline history={history} threshold={window.RAIN_THRESHOLD} dark={dark} nowSec={ref} />
        <div className="hero-foot">
          <div className="hero-meta">
            <span className="meta-label">MODELO</span>
            <span className="meta-val mono">CatBoost · Forte de Copacabana</span>
          </div>
          <div className="hero-meta">
            <span className="meta-label">TENDÊNCIA P</span>
            <span className={`meta-val mono ${pressureChange != null && pressureChange < -1 ? 'alert' : ''}`}>
              {pressureChange == null ? '—' : `${pressureChange >= 0 ? '+' : ''}${pressureChange.toFixed(2)} hPa/3h`}
            </span>
          </div>
        </div>
        <p className="hero-note">
          Treinado no nível do mar, no Rio. Fica no dataset como referência até existir um modelo da serra.
        </p>
      </section>

      {/* Telemetry strip */}
      <section className="telemetry">
        <div className="telemetry-row"><span className="t-key">LEITURA</span><span className="t-val mono">{localTime(latest.ts, true)} · {latest.time_src || '—'}</span></div>
        <div className="telemetry-row"><span className="t-key">ESTAÇÃO DO ANO</span><span className="t-val mono">{window.SEASON_PT[seasonKey]}</span></div>
        <div className="telemetry-row"><span className="t-key">TEMP. IR AMB.</span><span className="t-val mono">{latest.ambient_temperature != null ? `${latest.ambient_temperature.toFixed(1)} °C` : '—'}</span></div>
        <div className="telemetry-row"><span className="t-key">PAINEL / CARGA</span><span className="t-val mono">
          {latest.input_present == null ? '—' : latest.input_present ? 'com sol' : 'sem sol'} · {latest.charging == null ? '—' : latest.charging ? 'carregando' : 'parado'}
        </span></div>
      </section>

      {latest.latitude != null && latest.longitude != null && (
        <section className="gps-card">
          <div className="gps-head">
            <span className="gps-eyebrow">LOCALIZAÇÃO · ATGM336H</span>
            <span className="gps-sats mono">{latest.satellites != null ? `${latest.satellites} satélites` : 'fix'}</span>
          </div>
          <StationMap lat={latest.latitude} lon={latest.longitude} dark={dark} />
          <div className="gps-coords">
            <div className="gps-row"><span>LAT</span><span className="mono">{latest.latitude.toFixed(6)}</span></div>
            <div className="gps-row"><span>LON</span><span className="mono">{latest.longitude.toFixed(6)}</span></div>
            <div className="gps-row"><span>ALT</span><span className="mono">{fmt(latest.altitude_m, 0)} m · HDOP {fmt(latest.hdop, 1)}</span></div>
          </div>
        </section>
      )}

      <SensorHealth latest={latest} history={history} nowSec={nowSec} />
    </div>
  );
}

function SensorHealth({ latest, history, nowSec }) {
  const age = nowSec - latest.ts;
  const stale = age > 30 * 60;
  const ok = latest.sensors_ok || 0;
  // When each sensor last answered (within the loaded history)
  const lastOk = (test) => {
    for (let i = history.length - 1; i >= 0; i--) if (test(history[i])) return history[i].ts;
    return null;
  };
  const rows = window.SENSORS.map(s => ({
    id: s.id, label: s.label, ok: !!(ok & s.bit),
    last: lastOk(r => (r.sensors_ok || 0) & s.bit),
  }));
  rows.push({ id: 'GPS', label: 'Fix e relógio', ok: !!latest.gps_fix, last: lastOk(r => r.gps_fix) });
  const lastPhotoTs = lastOk(r => r.photo);
  rows.push({ id: 'Câmera', label: 'Foto no cartão', ok: lastPhotoTs != null && latest.ts - lastPhotoTs <= 3600, last: lastPhotoTs });
  const n = rows.filter(r => r.ok).length;

  return (
    <section className="health">
      <div className="section-head">
        <span className="section-title">SAÚDE DOS SENSORES</span>
        <span className="section-sub mono">{n}/{rows.length} na última leitura</span>
      </div>
      <div className="health-list">
        {rows.map(s => (
          <div className="health-row" key={s.id}>
            <span className={`health-dot ${!s.ok ? 'bad' : stale ? 'stale' : ''}`} />
            <span className="health-id mono">{s.id}</span>
            <span className="health-label">{s.label}</span>
            <span className="health-age mono">{s.ok ? fmtAge(age) : s.last ? `ok há ${fmtAge(nowSec - s.last)}` : 'falhou'}</span>
          </div>
        ))}
      </div>
    </section>
  );
}

// Leaflet map with CartoDB tiles that follow the theme.
function StationMap({ lat, lon, dark }) {
  const containerRef = useRef(null);
  const mapRef = useRef(null);
  const markerRef = useRef(null);
  const tileRef = useRef(null);

  useEffect(() => {
    if (!window.L || !containerRef.current) return;
    const L = window.L;
    if (!mapRef.current) {
      mapRef.current = L.map(containerRef.current, { zoomControl: true, attributionControl: true, scrollWheelZoom: false })
        .setView([lat, lon], 14);
    }
    // OpenStreetMap tiles (CARTO's basemaps now ask for an API key); the dark
    // theme inverts them with a CSS filter on .map-placeholder.dark
    if (!tileRef.current) {
      tileRef.current = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19, attribution: '© OpenStreetMap',
      }).addTo(mapRef.current);
    }
    if (!markerRef.current) {
      markerRef.current = L.circleMarker([lat, lon], { radius: 7, color: ACCENT, fillColor: ACCENT, fillOpacity: 0.9, weight: 2 })
        .addTo(mapRef.current);
    } else {
      markerRef.current.setLatLng([lat, lon]);
    }
    mapRef.current.setView([lat, lon]);
    setTimeout(() => mapRef.current && mapRef.current.invalidateSize(), 200);
  }, [lat, lon, dark]);

  useEffect(() => () => {
    if (mapRef.current) {
      mapRef.current.remove();
      mapRef.current = null; markerRef.current = null; tileRef.current = null;
    }
  }, []);

  return <div ref={containerRef} className={`map-placeholder ${dark ? 'dark' : ''}`} />;
}

// ── Gráficos tab ───────────────────────────────────────────────────────────

function ChartsTab({ history, dark, nowSec }) {
  const [hours, setHours] = useState(24);
  const ranges = [{ h: 6, l: '6h' }, { h: 24, l: '24h' }, { h: 48, l: '48h' }, { h: 168, l: '7d' }, { h: 720, l: '30d' }];
  const c = (h) => `oklch(0.78 0.10 ${h})`;
  const common = { data: history, hours, dark, nowSec };

  return (
    <div className="tab-content">
      <div className="range-bar">
        {ranges.map(r => (
          <button key={r.h} className={`range-btn ${hours === r.h ? 'active' : ''}`} onClick={() => setHours(r.h)}>{r.l}</button>
        ))}
      </div>
      <InstrumentChart {...common} label="TEMPERATURA" unit="°C" decimals={1}
        series={[{ key: 'temperature', color: c(30), label: 'SHT41' }]} />
      <InstrumentChart {...common} bounds={[0, 100]} label="UMIDADE" unit="%" decimals={0}
        series={[{ key: 'humidity', color: c(240) }]} />
      <InstrumentChart {...common} label="PRESSÃO" unit="hPa" decimals={1}
        series={[{ key: 'pressure', color: c(200) }]} />
      <InstrumentChart {...common} label="CHUVA" unit="mm" decimals={1} bars
        series={[{ key: 'rain_mm', color: 'oklch(0.72 0.12 230)' }]} />
      <InstrumentChart {...common} label="VENTO" unit="km/h" decimals={1} minZero
        series={[{ key: 'wind_speed_kmh', color: c(160), label: 'média' }, { key: 'wind_gust_kmh', color: ACCENT, label: 'rajada' }]} />
      <InstrumentChart {...common} label="LUZ" unit="lx" decimals={0} minZero
        series={[{ key: 'lux', color: c(95) }]} />
      <InstrumentChart {...common} bounds={[0, 100]} label="NEBULOSIDADE" unit="%" decimals={0}
        series={[{ key: 'cloud_cover', color: 'oklch(0.78 0.04 260)' }]} />
      <InstrumentChart {...common} label="IR CÉU × AMBIENTE" unit="°C" decimals={1}
        series={[{ key: 'sky_temperature', color: c(280), label: 'céu' }, { key: 'ambient_temperature', color: c(30), label: 'amb.' }]} />
      <InstrumentChart {...common} bounds={[0, 1]} label="PROB. CHUVA (MODELO)" unit="" decimals={2} minZero
        series={[{ key: 'rain_probability', color: ACCENT }]} />
      <InstrumentChart {...common} label="BATERIA" unit="V" decimals={2}
        series={[{ key: 'battery_voltage', color: 'oklch(0.74 0.14 145)' }]} />
    </div>
  );
}

// ── Câmera tab ─────────────────────────────────────────────────────────────

function CameraTab({ deviceId, photos, demo, loading }) {
  const [open, setOpen] = useState(null);
  const [shown, setShown] = useState(24);
  const list = useMemo(() => [...photos].reverse(), [photos]);

  useEffect(() => {
    if (open == null) return;
    const onKey = (e) => {
      if (e.key === 'Escape') setOpen(null);
      if (e.key === 'ArrowLeft') setOpen(i => Math.min(list.length - 1, i + 1));
      if (e.key === 'ArrowRight') setOpen(i => Math.max(0, i - 1));
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, list.length]);

  if (!list.length) {
    return (
      <div className="tab-content">
        <div className="empty-state">
          <span className="empty-eyebrow mono">CÂMERA</span>
          <p>{loading ? 'Carregando fotos…' : demo ? 'A demonstração não tem fotos.' : 'Nenhuma foto recebida nos últimos 7 dias.'}</p>
        </div>
      </div>
    );
  }

  const cur = open != null ? list[open] : null;
  return (
    <div className="tab-content">
      <div className="section-head cam-head">
        <span className="section-title">FOTOS · 7 DIAS</span>
        <span className="section-sub mono">{list.length} fotos</span>
      </div>
      <div className="photo-grid">
        {list.slice(0, shown).map((p, i) => (
          <button className="photo-cell" key={p.path} onClick={() => setOpen(i)}>
            <img loading="lazy" src={window.AS_API.photoUrl(deviceId, p.path)} alt="" />
            <span className="mono">{localTime(p.ts || p.uploaded_at, true)}</span>
          </button>
        ))}
      </div>
      {shown < list.length && (
        <button className="more-btn mono" onClick={() => setShown(shown + 24)}>CARREGAR MAIS ({list.length - shown})</button>
      )}

      {cur && (
        <div className="lightbox" onClick={() => setOpen(null)}>
          <img src={window.AS_API.photoUrl(deviceId, cur.path)} alt="" onClick={(e) => e.stopPropagation()} />
          <div className="lightbox-bar mono" onClick={(e) => e.stopPropagation()}>
            <button onClick={() => setOpen(Math.min(list.length - 1, open + 1))} disabled={open >= list.length - 1}>‹</button>
            <span>{localTime(cur.ts || cur.uploaded_at, true)} · {(cur.bytes / 1024).toFixed(0)} kB · seq {cur.seq}</span>
            <button onClick={() => setOpen(Math.max(0, open - 1))} disabled={open <= 0}>›</button>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Estação tab ────────────────────────────────────────────────────────────

function StationTab({ device, latest, demo, onToast, onIntervalSaved, theme, onThemeChange, dark, onLogout, nowSec }) {
  const current = device?.measure_interval_minutes ?? 5;
  const [interval, setIntervalVal] = useState(current);
  const [saving, setSaving] = useState(false);
  useEffect(() => setIntervalVal(current), [current]);

  const age = latest ? nowSec - latest.ts : null;
  const online = age != null && age < 30 * 60;
  const id = device?.device_id;

  const save = async () => {
    if (demo || !id) return;
    setSaving(true);
    try {
      await window.AS_API.setInterval(id, interval);
      onToast(`Intervalo de ${interval} min salvo. Vale na próxima conexão da estação.`);
      onIntervalSaved && onIntervalSaved();
    } catch (err) {
      onToast(`Erro: ${err.message}`);
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="tab-content">
      <section className="config-card">
        <div className="section-head">
          <span className="section-title">DISPOSITIVO</span>
          <span className={`status-pill ${online ? 'ok' : 'stale'}`}>{online ? 'ONLINE' : 'OFFLINE'}</span>
        </div>
        <div className="kv-list">
          <div className="kv-row"><span>ID</span><span className="mono">{id || '—'}</span></div>
          {device?.description && <div className="kv-row"><span>Local</span><span>{device.description}</span></div>}
          <div className="kv-row"><span>Firmware</span><span className="mono">{latest?.firmware ? `v${latest.firmware}` : '—'}</span></div>
          <div className="kv-row"><span>Bateria</span><span className="mono">
            {latest?.battery_voltage != null ? `${latest.battery_voltage.toFixed(2)} V` : '—'}
            {latest?.battery_percent != null ? ` · ${latest.battery_percent.toFixed(0)}%` : ''}
          </span></div>
          <div className="kv-row"><span>Última leitura</span><span className="mono">{latest ? `${localTime(latest.ts, true)} · há ${fmtAge(age)}` : '—'}</span></div>
          <div className="kv-row"><span>Fila no cartão</span><span className="mono">{latest?.queue_backlog != null ? `${latest.queue_backlog} leituras` : '—'}</span></div>
          <div className="kv-row"><span>Wi-Fi</span><span className="mono">{!latest ? '—' : latest.prev_wifi_rssi ? `${latest.prev_wifi_rssi} dBm` : 'sem conexão no ciclo anterior'}</span></div>
          <div className="kv-row"><span>Tempo acordada</span><span className="mono">{latest?.prev_awake_ms != null ? `${(latest.prev_awake_ms / 1000).toFixed(1)} s / ciclo` : '—'}</span></div>
          <div className="kv-row"><span>Ciclos desde ligar</span><span className="mono">{latest?.boot_count ?? '—'}</span></div>
          <div className="kv-row"><span>No dataset</span><span className="mono">{device ? `${device.readings} leituras · ${device.photos} fotos` : '—'}</span></div>
        </div>
      </section>

      <section className="config-card">
        <div className="section-head">
          <span className="section-title">DATASET</span>
          <span className="section-sub mono">CSV · UTC</span>
        </div>
        <div className="dl-row">
          <a className={`dl-btn mono ${demo ? 'disabled' : ''}`} href={demo ? undefined : window.AS_API.csvUrl(id, nowSec - 7 * 86400)}>7 DIAS</a>
          <a className={`dl-btn mono ${demo ? 'disabled' : ''}`} href={demo ? undefined : window.AS_API.csvUrl(id, nowSec - 30 * 86400)}>30 DIAS</a>
          <a className={`dl-btn mono ${demo ? 'disabled' : ''}`} href={demo ? undefined : window.AS_API.csvUrl(id)}>TUDO</a>
        </div>
        <p className="config-help">
          As mesmas 42 colunas do CSV no cartão, mais <span className="mono">device_id</span> e <span className="mono">received_at</span>.
          As fotos ficam no R2 <span className="mono">agrosense-dataset</span>.
        </p>
      </section>

      <section className="config-card">
        <div className="section-head"><span className="section-title">INTERVALO DE LEITURA</span></div>
        <div className="seg-control">
          {[1, 5, 10, 15, 30].map(v => (
            <button key={v} className={`seg-btn ${interval === v ? 'active' : ''}`} onClick={() => setIntervalVal(v)}>{v} min</button>
          ))}
        </div>
        <p className="config-help">
          A estação busca a configuração quando sobe dados, então a mudança vale a partir da próxima conexão Wi-Fi.
          Cada leitura tira uma foto: intervalos curtos gastam mais bateria e enchem o R2 mais rápido.
        </p>
        <button className={`save-btn ${saving ? 'saving' : ''}`} onClick={save} disabled={saving || demo || interval === current}>
          {demo ? 'INDISPONÍVEL NA DEMONSTRAÇÃO' : saving ? 'ENVIANDO…' : interval === current ? 'SALVO' : 'SALVAR INTERVALO'}
        </button>
      </section>

      <section className="config-card">
        <div className="section-head">
          <span className="section-title">APARÊNCIA</span>
          <span className="section-sub mono">{dark ? 'escuro' : 'claro'}</span>
        </div>
        <div className="seg-control">
          {[{ v: 'auto', l: 'auto' }, { v: 'light', l: 'claro' }, { v: 'dark', l: 'escuro' }].map(t => (
            <button key={t.v} className={`seg-btn ${theme === t.v ? 'active' : ''}`} onClick={() => onThemeChange(t.v)}>{t.l}</button>
          ))}
        </div>
      </section>

      <button className="logout-btn mono" onClick={onLogout}>{demo ? 'ENTRAR COM TOKEN' : 'SAIR (ESQUECER O TOKEN)'}</button>
      <div className="config-foot mono">Cloudflare Worker · D1 agrosense · R2 agrosense-dataset</div>
    </div>
  );
}

Object.assign(window, { WeatherTab, ChartsTab, CameraTab, StationTab });
