// ScreenLineDetail.jsx — dual-track strip, station amenity popups, closed/extended states, transfer tags

// Device tipo labels
const TIPO_LABEL = { 0: 'Ascensor', 1: 'Escalera mecánica', 2: 'Salvaescaleras' };

function StationPopup({ station, lineId, onClose }) {
  const detail = { ...STATION_DEFAULT, ...(STATION_DETAIL[station] || {}) };
  const crossings = CROSSINGS[station] || [];
  const isClosed = CLOSED_TODAY.includes(station);
  const isExtended = lineId === 'B' && B_EXTENDED_STATIONS.includes(station);
  const closedReason = CLOSED_REASONS[station];
  const singleDir = detail.singleDir || 0;
  // Only show device panel for stations with actual Emova mappings
  const hasElevation = (detail.elevator || detail.escalator) && (EMOVA_STATION_ID[lineId]?.[station] != null);

  // ── Emova device state ────────────────────────────────────────────────────
  const [devices, setDevices] = React.useState(undefined); // undefined=loading, []=loaded
  const [showDevices, setShowDevices] = React.useState(false);
  const [showInfo, setShowInfo] = React.useState(false);

  React.useEffect(() => {
    let cancelled = false;
    if (!hasElevation) { setDevices([]); return; }
    const cached = getEmovaDevices(lineId, station);
    if (cached !== null) { setDevices(cached); return; }
    loadEmovaStatus().then(() => {
      if (cancelled) return;
      setDevices(getEmovaDevices(lineId, station) || []);
    });
    return () => { cancelled = true; };
  }, [lineId, station]);

  // Derive presence + warning states from loaded Emova devices.
  // Emova is authoritative: if the API says a device is there, we show it
  // even if the static STATION_DETAIL doesn't list it.
  const hasElevatorDevices  = Array.isArray(devices) && devices.some(d => d.device_tipo === 0);
  const hasEscalatorDevices = Array.isArray(devices) && devices.some(d => d.device_tipo === 1);
  const showElevator   = detail.elevator  || hasElevatorDevices;
  const showEscalator  = detail.escalator || hasEscalatorDevices;
  const elevatorDown   = hasElevatorDevices && devices.some(d => d.device_tipo === 0 && !d.funcionando);
  const escalatorDown  = hasEscalatorDevices && devices.some(d => d.device_tipo === 1 && !d.funcionando);
  const anyDown        = elevatorDown || escalatorDown;
  const fetchedAt      = Array.isArray(devices) && devices[0]?.fetched_at;

  // ── Helpers ───────────────────────────────────────────────────────────────
  const fmtDate = (iso) => {
    if (!iso) return null;
    const d = new Date(iso);
    return d.toLocaleDateString('es-AR', { day: 'numeric', month: 'numeric', year: 'numeric' })
      + ' ' + d.toLocaleTimeString('es-AR', { hour: '2-digit', minute: '2-digit' });
  };

  // Amenity icon: state = 'ok' | 'warn' | 'off'
  const amenityIcon = (present, warn, label, icon) => {
    const state = !present ? 'off' : warn ? 'warn' : 'ok';
    const bg    = state === 'ok'   ? 'oklch(0.94 0.08 155)'
                : state === 'warn' ? 'oklch(0.96 0.12 85)'
                : 'var(--ink-100)';
    const clr   = state === 'ok'   ? 'var(--ok)'
                : state === 'warn' ? 'oklch(0.55 0.18 75)'
                : 'var(--text-dim)';
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '5px 0', opacity: present ? 1 : 0.38 }}>
        <div style={{ width: 28, height: 28, borderRadius: 8, background: bg, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <I name={icon} size={14} style={{ stroke: clr }} />
        </div>
        <span style={{ fontSize: 12, fontFamily: 'var(--font-display)', fontWeight: 600, color: present ? 'var(--text)' : 'var(--text-dim)' }}>{label}</span>
      </div>
    );
  };

  // ── Station info & artwork ─────────────────────────────────────────────────
  const stInfo   = getStationInfo(lineId, station);
  const artworks = getStationArtwork(lineId, station);
  const hasInfo  = !!(stInfo || artworks.length > 0);

  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 200, display: 'flex', alignItems: 'flex-end', justifyContent: 'center', background: 'rgba(0,0,0,.4)' }} onClick={onClose}>
      <div onClick={e => e.stopPropagation()} style={{ background: 'var(--surface)', borderRadius: '20px 20px 0 0', width: '100%', maxWidth: 420, padding: '20px 20px 32px', maxHeight: '82vh', overflowY: 'auto' }}>
        {/* handle */}
        <div style={{ width: 40, height: 4, background: 'var(--ink-200)', borderRadius: 2, margin: '0 auto 16px' }} />

        {/* Header */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
          <LineChip id={lineId} />
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 17 }}>{station}</div>
            {stInfo?.addr && (
              <div style={{ fontSize: 11, color: 'var(--text-dim)', marginTop: 1 }}>{stInfo.addr}</div>
            )}
            {isClosed && (
              <div style={{ fontSize: 11, color: 'var(--error)', fontFamily: 'var(--font-display)', fontWeight: 700, display: 'flex', alignItems: 'center', gap: 4, marginTop: 2 }}>
                <I name="x" size={12} style={{ stroke: 'var(--error)' }} /> {closedReason || 'Cerrada temporalmente'}
              </div>
            )}
            {isExtended && !isClosed && (
              <div style={{ fontSize: 11, color: 'var(--ok)', fontFamily: 'var(--font-display)', fontWeight: 700, display: 'flex', alignItems: 'center', gap: 4, marginTop: 2 }}>
                <I name="moon" size={12} style={{ stroke: 'var(--ok)' }} /> Horario extendido · hasta 2:30
              </div>
            )}
          </div>
          <button onClick={onClose} style={{ width: 30, height: 30, borderRadius: 15, background: 'var(--ink-100)', display: 'flex', alignItems: 'center', justifyContent: 'center', border: 'none', cursor: 'pointer' }}>
            <I name="x" size={16} />
          </button>
        </div>

        {/* Transfers */}
        {crossings.length > 0 && (
          <div style={{ marginBottom: 14 }}>
            <div style={{ fontSize: 11, fontFamily: 'var(--font-display)', fontWeight: 700, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '.08em', marginBottom: 6 }}>Combinaciones</div>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
              {crossings.map(id => (
                <div key={id} style={{ display: 'flex', alignItems: 'center', gap: 5, padding: '4px 10px 4px 5px', borderRadius: 999, background: 'var(--ink-100)' }}>
                  <LineChip id={id} size="sm" />
                  <span style={{ fontSize: 11, fontFamily: 'var(--font-display)', fontWeight: 700 }}>Línea {id}</span>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* Entrance direction note */}
        {singleDir > 0 && (
          <div style={{ marginBottom: 14, padding: '8px 12px', borderRadius: 10, background: 'oklch(0.97 0.03 75)', border: '1px solid oklch(0.88 0.08 75)', fontSize: 12, color: 'oklch(0.4 0.12 75)', fontFamily: 'var(--font-display)', fontWeight: 600 }}>
            ⚠ {singleDir === 1 ? 'Alguna boca solo permite acceso en una dirección.' : 'Algunas bocas son de salida únicamente.'}
          </div>
        )}

        {/* Openings count */}
        {detail.openings && (
          <div style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: 12 }}>
            {detail.openings} boca{detail.openings !== 1 ? 's' : ''} de acceso
          </div>
        )}

        {/* Amenities */}
        <div style={{ fontSize: 11, fontFamily: 'var(--font-display)', fontWeight: 700, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '.08em', marginBottom: 4 }}>Servicios</div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0 12px' }}>
          {amenityIcon(detail.accessible,  false,          'Accesible',         'accessibility')}
          {amenityIcon(showElevator,   elevatorDown,   'Ascensor',          'arrowUp')}
          {amenityIcon(showEscalator,  escalatorDown,  'Escalera mecánica', 'list')}
          {amenityIcon(detail.baths,       false,          'Baños',             'info')}
          {amenityIcon(detail.wifi,        false,          'Wi-Fi',             'wifi')}
          {amenityIcon(detail.office,      false,          'Boletería',         'user')}
          {amenityIcon(detail.atm,         false,          'Cajero',            'star')}
        </div>

        {/* ── Action buttons row (Detalle de accesos / + info) ─────────────── */}
        {(hasElevation || hasInfo) && (
          <div style={{ display: 'flex', gap: 8, marginTop: 14, flexWrap: 'wrap' }}>
            {hasElevation && (
              <button
                onClick={() => setShowDevices(v => !v)}
                style={{ display: 'flex', alignItems: 'center', gap: 5, padding: '6px 12px', borderRadius: 20, background: showDevices ? 'var(--ink-200)' : 'var(--ink-100)', border: 'none', cursor: 'pointer', fontSize: 12, fontFamily: 'var(--font-display)', fontWeight: 700, color: anyDown ? 'oklch(0.45 0.16 75)' : 'var(--text)' }}
              >
                {anyDown && <I name="warning" size={12} style={{ stroke: 'oklch(0.55 0.18 75)' }} />}
                Detalle de accesos
                <I name="chevronDown" size={12} style={{ transition: 'transform .2s', transform: showDevices ? 'rotate(180deg)' : 'none' }} />
              </button>
            )}
            {hasInfo && (
              <button
                onClick={() => setShowInfo(v => !v)}
                style={{ display: 'flex', alignItems: 'center', gap: 5, padding: '6px 12px', borderRadius: 20, background: showInfo ? 'var(--ink-200)' : 'var(--ink-100)', border: 'none', cursor: 'pointer', fontSize: 12, fontFamily: 'var(--font-display)', fontWeight: 700, color: 'var(--text)' }}
              >
                + info
                <I name="chevronDown" size={12} style={{ transition: 'transform .2s', transform: showInfo ? 'rotate(180deg)' : 'none' }} />
              </button>
            )}
          </div>
        )}

        {/* ── Detalle de accesos panel ──────────────────────────────────────── */}
        {showDevices && (
          <div style={{ marginTop: 12, borderRadius: 12, background: 'var(--ink-100)', padding: '10px 12px' }}>
            {/* data freshness */}
            {fetchedAt && (
              <div style={{ fontSize: 10, color: 'var(--text-dim)', marginBottom: 8, textAlign: 'right' }}>
                Datos: {fmtDate(fetchedAt)}
              </div>
            )}
            {/* closed station override */}
            {isClosed ? (
              <div style={{ fontSize: 12, color: 'var(--error)', fontFamily: 'var(--font-display)', fontWeight: 700, padding: '6px 0', display: 'flex', alignItems: 'center', gap: 6 }}>
                <I name="x" size={13} style={{ stroke: 'var(--error)' }} />
                {closedReason || 'Estación cerrada temporalmente'}
              </div>
            ) : devices === undefined ? (
              <div style={{ fontSize: 12, color: 'var(--text-dim)', padding: '6px 0' }}>Cargando estado de ascensores…</div>
            ) : !devices || devices.length === 0 ? (
              <div style={{ fontSize: 12, color: 'var(--text-dim)', padding: '6px 0' }}>Sin datos disponibles para esta estación.</div>
            ) : (
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                {devices.map((d, i) => {
                  const isDown = !d.funcionando;
                  return (
                    <div key={i} style={{ borderRadius: 8, background: isDown ? 'oklch(0.97 0.04 25)' : 'var(--surface)', border: isDown ? '1px solid oklch(0.88 0.1 25)' : '1px solid var(--ink-200)', padding: '8px 10px' }}>
                      <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 13, color: isDown ? 'oklch(0.45 0.18 25)' : 'var(--text)', marginBottom: 3 }}>
                        {TIPO_LABEL[d.device_tipo] || 'Dispositivo'} · {d.device_name}
                      </div>
                      {d.descripcion && (
                        <div style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: 3 }}>{d.descripcion}</div>
                      )}
                      <div style={{ fontSize: 11, color: isDown ? 'oklch(0.5 0.15 25)' : 'var(--ok)', fontFamily: 'var(--font-display)', fontWeight: 700 }}>
                        Estado: {d.funcionando ? 'Funcionando' : 'Fuera de servicio'}
                      </div>
                      {d.fecha_actualizacion && (
                        <div style={{ fontSize: 11, color: 'var(--text-dim)', marginTop: 2 }}>
                          Última actualización: {fmtDate(d.fecha_actualizacion)}
                        </div>
                      )}
                      {d.sentido && (
                        <div style={{ fontSize: 11, color: 'var(--text-dim)', marginTop: 1 }}>
                          Sentido: {d.sentido}
                        </div>
                      )}
                      {d.fecha_normalizacion && isDown && (
                        <div style={{ fontSize: 11, color: 'oklch(0.5 0.12 75)', marginTop: 2 }}>
                          Normalización estimada: {fmtDate(d.fecha_normalizacion)}
                        </div>
                      )}
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        )}

        {/* ── + info panel ──────────────────────────────────────────────────── */}
        {showInfo && (
          <div style={{ marginTop: 12, borderRadius: 12, background: 'var(--ink-100)', padding: '10px 12px' }}>
            {stInfo?.desc && (
              <p style={{ fontSize: 12, color: 'var(--text-muted)', lineHeight: 1.55, margin: '0 0 10px' }}>
                {stInfo.desc}
              </p>
            )}
            {artworks.length > 0 && (
              <div>
                <div style={{ fontSize: 11, fontFamily: 'var(--font-display)', fontWeight: 700, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '.08em', marginBottom: 8 }}>
                  Arte en la estación
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                  {artworks.map((aw, i) => (
                    <div key={i} style={{ fontSize: 12 }}>
                      <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, color: 'var(--text)' }}>{aw.a}</div>
                      {aw.t && <div style={{ color: 'var(--text-muted)', marginTop: 1, lineHeight: 1.4 }}>{aw.t}</div>}
                    </div>
                  ))}
                </div>
              </div>
            )}
            {!stInfo && artworks.length === 0 && (
              <div style={{ fontSize: 12, color: 'var(--text-dim)' }}>Sin información adicional disponible.</div>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

// Finds the station index on `crossLineId` that corresponds to the interchange with
// `currentLineId`. First tries a direct name match, then does a reverse CROSSINGS lookup:
// finds a station on crossLineId whose CROSSINGS entry lists currentLineId.
function getTransferStationIdx(s, crossLineId, currentLineId) {
  const xLine = LINES.find(l => l.id === crossLineId);
  if (!xLine) return -1;
  const direct = xLine.stations.indexOf(s);
  if (direct >= 0) return direct;
  for (const [st, lineIds] of Object.entries(CROSSINGS)) {
    if (lineIds.includes(currentLineId) && xLine.stations.includes(st)) {
      return xLine.stations.indexOf(st);
    }
  }
  return -1;
}

// Returns SECONDS until next train at station `idx` in direction dir (0=fwd, 1=bwd).
// Uses Date.now() — same epoch-based time as the train animation — so chips and icons stay in sync.
// stationT = interStation + dwellSecs (must match trainPos calculation).
// Returns 0 ("●") for 30 s after departure (phase < 30) or when train is arriving (secs ≈ 0).
const AT_STATION_LINGER_SECS = 30;
function getNextArrival(idx, stationCount, stationT, headwaySecs, dir) {
  const nowSecs = Date.now() / 1000;
  const stationOffset = (dir === 0 ? idx : (stationCount - 1 - idx)) * stationT;
  const phase = ((nowSecs - stationOffset) % headwaySecs + headwaySecs) % headwaySecs;
  const remaining = Math.round(headwaySecs - phase);
  // Show dot while train just departed (phase < linger) or is arriving (remaining ≤ 0)
  if (phase < AT_STATION_LINGER_SECS || remaining <= 0) return 0;
  return remaining;
}

// Is current time peak hour? (8:30-10:00 or 17:30-19:00)
function isPeakHour() {
  const now = new Date();
  const mins = now.getHours() * 60 + now.getMinutes();
  return (mins >= 510 && mins < 600) || (mins >= 1050 && mins < 1140);
}

function ScreenLineDetail({ lineId, go, t, pushPrefs, setPushPrefs }) {
  const line = LINES.find(l => l.id === lineId) || LINES[0];
  const lineStatus = useLiveStatus();
  const st = lineStatus[lineId];
  const { isStale } = useLiveFreshness();
  const svc = getServiceState(lineId);
  const serviceClosed = svc.state === 'closed';
  const closingSoon = svc.state === 'closing-soon';
  const minsLeft = closingSoon ? Math.max(1, Math.round(svc.minsUntilClose)) : null;
  // When service is closed, override the alert note with first-train info
  const noteText = serviceClosed
    ? `Fin del servicio (cerró ${svc.closedSince}). Primer tren ${svc.opensDay} a las ${svc.opensAt}.`
    : st.note;

  // NOTA: Los anuncios del CMS NO se muestran en el detalle de línea.
  // Solo se muestran en ScreenMain (página principal) y en ScreenNews (histórico).
  // Acá solo mostraríamos alertas de Emova/infosubte si las incorporamos directamente
  // al objeto `st` (status de la línea), pero por ahora lo dejamos sin anuncios de línea
  // para mantenerlo limpio y enfocado en el estado/información de la línea.
  const isLightLine = lineId === 'H' || lineId === 'P';
  const isY = isLightLine; // keep alias for compat
  const txt = isLightLine ? '#0B1220' : '#fff';
  const gradClass = 'grad-' + lineId.toLowerCase();
  const c2 = `var(--linea-${lineId.toLowerCase()}-2)`;

  // Tick every second for countdown
  const [, setTick] = React.useState(0);
  const [selectedStation, setSelectedStation] = React.useState(null);
  const [scrolled, setScrolled] = React.useState(false);
  const scrollRef = React.useRef(null);

  // Push notification prefs for this line — uses App-level pushPrefs (synced via localStorage)
  const prefs = pushPrefs || PUSH_DEFAULTS;
  const lineNotifEnabled = !!(prefs.lines?.[lineId]);
  const [showNotifModal, setShowNotifModal] = React.useState(false);

  const toggleLineNotif = async () => {
    if (!lineNotifEnabled) {
      if ('Notification' in window) {
        const perm = await Notification.requestPermission();
        if (perm !== 'granted') return;
      }
      setShowNotifModal(true);
    } else {
      setPushPrefs(p => ({ ...p, lines: { ...(p.lines || {}), [lineId]: false } }));
    }
  };
  const saveNotifConfig = (config) => {
    setPushPrefs(p => ({
      ...p,
      lines: { ...(p.lines || {}), [lineId]: true },
      lineConfig: { ...(p.lineConfig || {}), [lineId]: config },
    }));
    setShowNotifModal(false);
  };

  React.useEffect(() => {
    const i = setInterval(() => setTick(x => x + 1), 1000);
    return () => clearInterval(i);
  }, []);

  React.useEffect(() => {
    const el = scrollRef.current;
    if (!el) return;
    const fn = () => setScrolled(el.scrollTop > 60);
    el.addEventListener('scroll', fn, { passive: true });
    return () => el.removeEventListener('scroll', fn);
  }, []);

  const stationCount = line.stations.length;

  // Stations mentioned in the live alert note (e.g. "Estación Piedras cerrada")
  const affectedStations = React.useMemo(() => {
    if (!noteText || serviceClosed) return new Set();
    const lower = noteText.toLowerCase();
    return new Set(line.stations.filter(s => lower.includes(s.toLowerCase())));
  }, [noteText, serviceClosed, line.stations]);

  // Check if last train of the day already passed station idx in direction dir
  function lastTrainPassed(idx, dir) {
    const ext = lineId === 'B' && B_EXTENDED_STATIONS.includes(line.stations[idx]);
    const lastH = getLastTrainTime(lineId, idx, stationCount, line.travelTime, dir, new Date(), ext);
    if (lastH == null) return false;
    const nowH = new Date().getHours() + new Date().getMinutes()/60 + new Date().getSeconds()/3600;
    return nowH > lastH;
  }

  // Should we hide all live predictions? (service closed OR data stale > 2 min)
  const hidePredictions = serviceClosed || isStale;

  const headwaySecs = st.freqSecs || 300;
  const dwellSecs = isPeakHour() ? 30 : 20;
  const travelTimeSecs = (line.travelTime || 20) * 60;
  const interStation = travelTimeSecs / (stationCount - 1);
  const stationT = interStation + dwellSecs; // seconds per station including dwell (used by fallback)

  // ── Real trains from GTFS-RT (via get_trips_with_schedule) ───────────────
  // Each train corresponds to a specific stop in the forecast; stationIdx is a
  // fractional index in line.stations so it can be placed on the vertical track.
  const { trains: realTrains, nextArrivalSecs, hasData: hasRealData } = useRealTrains(lineId);

  // Build per-direction lists of fractional positions. Direction_id 0 = heading
  // one way, 1 = the other. We map direction → fwd/bwd relative to line.stations[0].
  // For lines in this system, the mapping is consistent: direction 0 travels from
  // stations[0] toward stations[last] (forward), direction 1 goes backward.
  let fwdPositions = [];
  let bwdPositions = [];

  if (hasRealData && !hidePredictions) {
    realTrains.forEach(tr => {
      if (tr.direction_id === 0) fwdPositions.push(tr.stationIdx);
      else                       bwdPositions.push(tr.stationIdx);
    });
  } else if (!hidePredictions) {
    // Synthetic fallback (for lines without GTFS-RT data: C, H, U, P)
    const cycleSecs = travelTimeSecs + stationCount * dwellSecs;
    const nowSecs   = Date.now() / 1000;
    const trainPos = (phaseOffset) => {
      const t = ((nowSecs + phaseOffset) % cycleSecs + cycleSecs) % cycleSecs;
      const idx = t / stationT;
      return idx < stationCount ? idx : null;
    };
    const trainsPerDir = Math.ceil(travelTimeSecs / headwaySecs) + 1;
    fwdPositions = Array.from({ length: trainsPerDir }, (_, i) => trainPos(i * headwaySecs))
      .filter(p => p !== null);
    bwdPositions = Array.from({ length: trainsPerDir }, (_, i) => {
      const p = trainPos(i * headwaySecs + headwaySecs * 0.37);
      return p !== null ? (stationCount - 1) - p : null;
    }).filter(p => p !== null);
  }

  const TRACK_OFFSET = 12; // px between the two parallel tracks (closer = more homogeneous)
  const TOTAL_W = TRACK_OFFSET + 12; // total width of dual-track zone

  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
      {/* ── Header — collapses on scroll ── */}
      <div className={gradClass} style={{
        padding: scrolled ? '6px 16px 8px' : '8px 16px 20px',
        color: txt, flexShrink: 0,
        transition: 'padding .22s ease',
      }}>
        {/* Nav row — always visible */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '4px 0' }}>
          <button onClick={() => go('main')} style={{ width: 36, height: 36, borderRadius: 18, background: 'rgba(255,255,255,.22)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: txt, flexShrink: 0 }}>
            <I name="arrowLeft" size={18} />
          </button>
          {/* Compact: letter badge + line name */}
          <div style={{ flex: 1, display: 'flex', alignItems: 'center', gap: scrolled ? 8 : 0, minWidth: 0, overflow: 'hidden' }}>
            <div style={{
              width: scrolled ? 30 : 0, height: scrolled ? 30 : 0,
              borderRadius: 8, background: 'rgba(255,255,255,.22)',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 16, color: txt,
              flexShrink: 0, overflow: 'hidden',
              transition: 'width .22s ease, height .22s ease',
            }}>{lineId}</div>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: scrolled ? 15 : 14, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
              {line.name}
            </div>
          </div>
          {/* Right: freq (compact only) + heart */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexShrink: 0 }}>
            <div style={{
              fontSize: 12, fontFamily: 'var(--font-display)', fontWeight: 700,
              maxWidth: scrolled ? 80 : 0, overflow: 'hidden', whiteSpace: 'nowrap',
              transition: 'max-width .22s ease', opacity: scrolled ? .9 : 0,
            }}>{st.freq}</div>
            <button onClick={toggleLineNotif} title={lineNotifEnabled ? 'Desactivar alertas' : 'Activar alertas'} style={{ width: 36, height: 36, borderRadius: 18, background: lineNotifEnabled ? 'rgba(255,255,255,.45)' : 'rgba(255,255,255,.22)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: lineNotifEnabled ? (isLightLine ? '#0B1220' : 'white') : txt }}>
              <I name="heart" size={18} style={{ fill: lineNotifEnabled ? 'currentColor' : 'none' }} />
            </button>
          </div>
        </div>

        {/* Full content — slides out when scrolled */}
        <div style={{
          maxHeight: scrolled ? 0 : 240,
          opacity: scrolled ? 0 : 1,
          overflow: 'hidden',
          transition: 'max-height .25s ease, opacity .18s ease',
          pointerEvents: scrolled ? 'none' : 'auto',
        }}>
          <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginTop: 10 }}>
            <div>
              <div style={{ fontSize: 11, opacity: .8, textTransform: 'uppercase', letterSpacing: '.1em', fontWeight: 700 }}>Línea</div>
              <div style={{ fontSize: 72, fontWeight: 800, fontFamily: 'var(--font-display)', lineHeight: 1, letterSpacing: '-.04em' }}>{lineId}</div>
            </div>
            <div style={{ textAlign: 'right' }}>
              <div style={{ fontSize: 11, opacity: .8, textTransform: 'uppercase', letterSpacing: '.1em', fontWeight: 700 }}>{t('frequency')}</div>
              <div style={{ fontSize: 28, fontFamily: 'var(--font-display)', fontWeight: 800, lineHeight: 1 }}>{st.freq}</div>
            </div>
          </div>
          <div style={{ marginTop: 14, display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            <div style={{ padding: '6px 10px', borderRadius: 8, background: 'rgba(255,255,255,.22)', fontSize: 11, fontWeight: 700, fontFamily: 'var(--font-display)' }}>
              {serviceClosed ? '🌙 Fin del servicio'
                : isStale     ? '… Sin datos en vivo'
                : st.state === 'normal' ? '✓ ' + t('normalService')
                : '⚠ ' + (st.state === 'delay' ? t('delays') : t('alert'))}
            </div>
            {lineId === 'B' && (
              <div style={{ padding: '6px 10px', borderRadius: 8, background: 'rgba(255,255,255,.22)', fontSize: 11, fontWeight: 700, fontFamily: 'var(--font-display)', display: 'flex', alignItems: 'center', gap: 4 }}>
                <I name="moon" size={12} /> Horario extendido en estaciones clave
              </div>
            )}
          </div>
        </div>

        {/* Closing-soon banner — pesimista, titilando, con detalle por sentido */}
        {closingSoon && !serviceClosed && (() => {
          // último tren parte del terminal a (close - travelTime); arriba a la otra cabecera a `close`
          const lastDepartH = (() => {
            const w = getServiceWindow(lineId); if (!w) return null;
            return w.close - (line.travelTime / 60);
          })();
          const lastDepart = lastDepartH != null ? decimalHourToLabel(lastDepartH) : '—';
          return (
            <div className="blink" style={{ marginTop: scrolled ? 4 : 10, padding: scrolled ? '6px 10px' : '10px 12px', borderRadius: 10, background: 'rgba(255,107,107,.28)', border: '1px solid rgba(255,255,255,.35)', fontFamily: 'var(--font-display)' }}>
              <div style={{ fontSize: scrolled ? 12 : 13, fontWeight: 800 }}>⚠ Último tren en {minsLeft} min</div>
              {!scrolled && (
                <div style={{ fontSize: 11, marginTop: 4, lineHeight: 1.35, fontWeight: 600, opacity: .95 }}>
                  ↓ {line.head2}: último sale de {line.head1} a las {lastDepart}<br/>
                  ↑ {line.head1}: último sale de {line.head2} a las {lastDepart}
                </div>
              )}
            </div>
          );
        })()}

        {/* Alert note — visible in both states (taller when scrolled + alert) */}
        {noteText && (
          <div style={{ marginTop: scrolled ? 4 : 10, padding: scrolled ? '5px 10px' : 10, borderRadius: 10, background: 'rgba(0,0,0,.18)', fontSize: scrolled ? 11 : 12 }}>
            {noteText}
          </div>
        )}
      </div>

      {/* station strip */}
      {/* paddingBottom ensures enough overflow to always trigger header collapse */}
      <div ref={scrollRef} className="scroll-area" style={{ padding: '16px 16px 0' }}>
      <div style={{ paddingBottom: 'max(24px, 30vh)' }}>
        {/* Direction legend */}
        <div style={{ display: 'flex', gap: 8, marginBottom: 14 }}>
          <div style={{ flex: 1, padding: '6px 10px', borderRadius: 8, background: 'var(--surface)', border: `2px solid ${c2}`, display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, fontFamily: 'var(--font-display)', fontWeight: 700 }}>
            <div style={{ width: 6, height: 24, borderRadius: 3, background: c2 }} />
            <div>
              <div style={{ fontSize: 9, color: 'var(--text-muted)', textTransform: 'uppercase' }}>via 1 ↓</div>
              <div>{line.head1} → {line.head2}</div>
            </div>
          </div>
          <div style={{ flex: 1, padding: '6px 10px', borderRadius: 8, background: 'var(--surface)', border: `2px solid ${c2}`, display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, fontFamily: 'var(--font-display)', fontWeight: 700 }}>
            <div style={{ width: 6, height: 24, borderRadius: 3, background: c2 }} />
            <div>
              <div style={{ fontSize: 9, color: 'var(--text-muted)', textTransform: 'uppercase' }}>via 2 ↑</div>
              <div>{line.head2} → {line.head1}</div>
            </div>
          </div>
        </div>

        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
          <span style={{ fontSize: 11, color: 'var(--text-muted)', fontFamily: 'var(--font-display)', fontWeight: 600 }}>
            {stationCount} estaciones · {line.travelTime} min punta a punta
            {(() => {
              const nowH = new Date().getHours() + new Date().getMinutes()/60;
              // Last train for each direction departing from each terminal
              // Para B, los terminales son estaciones extendidas → usar horario extendido (dir-específico)
              const termExt = lineId === 'B';
              const fwdLastH = getLastTrainTime(lineId, 0, stationCount, line.travelTime, 0, new Date(), termExt);
              const bwdLastH = getLastTrainTime(lineId, stationCount - 1, stationCount, line.travelTime, 1, new Date(), termExt);
              const fwdPassed = fwdLastH != null && nowH > fwdLastH;
              const bwdPassed = bwdLastH != null && nowH > bwdLastH;
              const parts = [];
              if (fwdLastH != null && !fwdPassed) parts.push(`↓ ${line.head1} ${decimalHourToLabel(fwdLastH)}`);
              if (bwdLastH != null && !bwdPassed) parts.push(`↑ ${line.head2} ${decimalHourToLabel(bwdLastH)}`);
              if (parts.length === 0) return ' · tocá para más info';
              return ' · último: ' + parts.join(' · ');
            })()}
          </span>
        </div>

        {/* Dual-track + stations */}
        <div style={{ position: 'relative', paddingLeft: TOTAL_W + 12 }}>

          {/* Track 1 — forward */}
          <div style={{
            position: 'absolute', left: 6, top: 10, bottom: 10, width: 5, borderRadius: 3,
            background: `linear-gradient(180deg, var(--linea-${lineId.toLowerCase()}-1), var(--linea-${lineId.toLowerCase()}-2))`
          }} />
          {/* Track 2 — backward (same color, slightly faded) */}
          <div style={{
            position: 'absolute', left: 6 + TRACK_OFFSET, top: 10, bottom: 10, width: 5, borderRadius: 3,
            background: `linear-gradient(180deg, var(--linea-${lineId.toLowerCase()}-1), var(--linea-${lineId.toLowerCase()}-2))`,
            opacity: .9
          }} />

          {/* Live train icons hidden — representación en vivo desactivada */}

          {/* Station rows */}
          {line.stations.map((s, i) => {
            const isHead = i === 0 || i === stationCount - 1;
            const isClosed = CLOSED_TODAY.includes(s);
            const isAffected = !isClosed && affectedStations.has(s);
            const isExtended = lineId === 'B' && B_EXTENDED_STATIONS.includes(s);
            const crossings = CROSSINGS[s] || [];
            const detail = { ...STATION_DEFAULT, ...(STATION_DETAIL[s] || {}) };
            const singleDir = detail.singleDir || 0;

            return (
              <button
                key={s}
                onClick={() => setSelectedStation(s)}
                style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '9px 0', minHeight: 44, width: '100%', textAlign: 'left', background: 'transparent', opacity: isClosed ? .55 : 1, position: 'relative', overflow: 'visible' }}
              >
                {/* Crossing line color strips — come from the left edge, fade into the content */}
                {crossings.map((xId, xi) => {
                  const xColor = `var(--linea-${xId.toLowerCase()}-2)`;
                  const stripH = 3;
                  const totalStrips = crossings.length;
                  // vertically center multiple strips around the row midpoint
                  const offset = (xi - (totalStrips - 1) / 2) * (stripH + 3);
                  return (
                    <div key={xId} style={{
                      position: 'absolute',
                      left: -53,
                      top: `calc(50% + ${offset}px)`,
                      transform: 'translateY(-50%)',
                      width: 51,
                      height: 7,
                      borderRadius: 7,
                      background: `linear-gradient(90deg, ${xColor} 0%, ${xColor} 40%, transparent 100%)`,
                      opacity: 1,
                      pointerEvents: 'none',
                      zIndex: 0,
                    }} />
                  );
                })}
                {/* Station dot(s): one centred circle for shared access, two small for separate */}
                {singleDir === 0 ? (
                  // Shared entrance → one circle centred between the two tracks
                  <div style={{
                    position: 'absolute',
                    left: isHead ? -31 : -29,
                    width: isHead ? 18 : 14, height: isHead ? 18 : 14,
                    borderRadius: '50%',
                    background: isClosed ? 'var(--ink-200)' : 'white',
                    border: `3px solid ${isClosed ? 'var(--ink-300)' : isAffected ? 'var(--warn)' : c2}`,
                    zIndex: 2,
                  }} />
                ) : (
                  // Separate directional access → one dot per track
                  <>
                    <div style={{
                      position: 'absolute', left: -35, width: 13, height: 13, borderRadius: '50%',
                      background: isClosed ? 'var(--ink-200)' : 'white',
                      border: `2.5px solid ${isClosed ? 'var(--ink-300)' : c2}`,
                      zIndex: 2,
                    }} />
                    <div style={{
                      position: 'absolute', left: -35 + TRACK_OFFSET, width: 13, height: 13, borderRadius: '50%',
                      background: 'var(--bg)',
                      border: `2.5px solid ${isClosed ? 'var(--ink-300)' : c2}`,
                      zIndex: 2, opacity: .65,
                    }} />
                  </>
                )}

                {/* Name + badges */}
                <div style={{ flex: 1, display: 'flex', justifyContent: 'space-between', alignItems: 'center', minWidth: 0 }}>
                  <div style={{ minWidth: 0 }}>
                    <div style={{ fontFamily: 'var(--font-display)', fontWeight: isHead ? 800 : 600, fontSize: 14, display: 'flex', alignItems: 'center', gap: 6 }}>
                      <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{s}</span>
                      {isExtended && !isClosed && (
                        <span style={{ fontSize: 9, padding: '1px 5px', borderRadius: 4, background: 'oklch(0.94 0.07 155)', color: 'var(--ok)', fontFamily: 'var(--font-display)', fontWeight: 700, flexShrink: 0 }}>🌙 EXT</span>
                      )}
                    </div>
                    {isHead && <div style={{ fontSize: 10, color: 'var(--text-muted)' }}>cabecera</div>}
                    {isClosed && <div style={{ fontSize: 10, color: 'var(--error)', fontWeight: 700 }}>CERRADA</div>}
                    {isAffected && <div style={{ fontSize: 10, color: 'oklch(0.45 0.15 75)', fontWeight: 700 }}>⚠ afectada por novedad</div>}
                    {singleDir > 0 && !isClosed && (
                      <div style={{ fontSize: 9, color: 'var(--warn)', fontWeight: 700 }}>↓ boca con dirección limitada</div>
                    )}
                  </div>
                  <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4, flexShrink: 0 }}>
                    {/* Transfer chips + next train times (both dirs) for each transfer line */}
                    {crossings.length > 0 && (
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 4, alignItems: 'flex-end' }}>
                        {crossings.map(xId => {
                          const xLine = LINES.find(l => l.id === xId);
                          const xStatus = lineStatus[xId];
                          if (!xLine || !xStatus) return <LineChip key={xId} id={xId} size="sm" />;
                          const xIdx = getTransferStationIdx(s, xId, lineId);
                          const xColor = xLine.color2 || '#888';
                          const xDark = xId === 'H' || xId === 'P';
                          let fwdSecs = null, bwdSecs = null;
                          if (xIdx >= 0 && !hidePredictions && getServiceState(xId).state !== 'closed') {
                            const xHeadway = xStatus.freqSecs || 300;
                            const xStCount = xLine.stations.length;
                            const xStationT = (xLine.travelTime || 20) * 60 / Math.max(1, xStCount - 1) + (isPeakHour() ? 30 : 20);
                            fwdSecs = getNextArrival(xIdx, xStCount, xStationT, xHeadway, 0);
                            bwdSecs = getNextArrival(xIdx, xStCount, xStationT, xHeadway, 1);
                          }
                          return (
                            <div key={xId} style={{ display: 'flex', alignItems: 'center', gap: 3 }}>
                              <LineChip id={xId} size="sm" />
                              {fwdSecs != null
                                ? <NextTrainChip secs={fwdSecs} dir="↓" lineHex={xColor} darkText={xDark} />
                                : <div style={{ width: 42, height: 22, opacity: 0 }} />}
                              {bwdSecs != null
                                ? <NextTrainChip secs={bwdSecs} dir="↑" lineHex={xColor} darkText={xDark} faded />
                                : <div style={{ width: 42, height: 22, opacity: 0 }} />}
                            </div>
                          );
                        })}
                      </div>
                    )}
                    {/* Next train chips (dir0 ↓ and dir1 ↑) — hidden if station closed,
                        service closed, data stale, or last train already passed in that dir.
                        Fixed 2-column grid so the ↓ chip always occupies the left slot and
                        ↑ the right slot; absent chips leave an empty placeholder so the
                        layout never shifts. */}
                    {!isClosed && (() => {
                      // Always render the 2-col grid so layout never shifts.
                      // Chips invisible when: service closed, stale data, or last train passed.
                      const showFwd = !hidePredictions && !lastTrainPassed(i, 0);
                      const showBwd = !hidePredictions && !lastTrainPassed(i, 1);
                      // Use real data when available; fall back to synthetic if real is null
                      // or implausibly far (> 2× headway, which means the train is well past
                      // and the next one isn't in the schedule window yet).
                      const realFwd = hasRealData ? nextArrivalSecs(i, 0) : null;
                      const realBwd = hasRealData ? nextArrivalSecs(i, 1) : null;
                      const synthFwd = getNextArrival(i, stationCount, stationT, headwaySecs, 0);
                      const synthBwd = getNextArrival(i, stationCount, stationT, headwaySecs, 1);
                      const fwdSecs = showFwd ? (realFwd != null && realFwd <= headwaySecs * 2 ? realFwd : synthFwd) : null;
                      const bwdSecs = showBwd ? (realBwd != null && realBwd <= headwaySecs * 2 ? realBwd : synthBwd) : null;
                      return (
                        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4 }}>
                          {fwdSecs != null
                            ? <NextTrainChip secs={fwdSecs} dir="↓" lineHex={line.color2} darkText={isY} />
                            : <div style={{ width:42, height:22, opacity:0 }} />}
                          {bwdSecs != null
                            ? <NextTrainChip secs={bwdSecs} dir="↑" lineHex={line.color2} darkText={isY} faded />
                            : <div style={{ width:42, height:22, opacity:0 }} />}
                        </div>
                      );
                    })()}
                    {/* Amenity icons */}
                    <div style={{ display: 'flex', gap: 3 }}>
                      {detail.accessible && <I name="accessibility" size={12} style={{ stroke: 'var(--ok)' }} />}
                      {detail.elevator && <I name="arrowUp" size={12} style={{ stroke: 'var(--info)' }} />}
                    </div>
                  </div>
                </div>
              </button>
            );
          })}
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginTop: 16 }}>
          <button className="btn btn-secondary" onClick={() => go('map')}><I name="map" size={16} />Ver en mapa</button>
          <button className="btn btn-primary" onClick={() => go('incidentReport', { line: lineId })}><I name="warning" size={16} />{t('report')}</button>
        </div>
      </div>{/* end minHeight wrapper */}
      </div>{/* end scroll-area */}

      {/* Station popup */}
      {selectedStation && (
        <StationPopup station={selectedStation} lineId={lineId} onClose={() => setSelectedStation(null)} />
      )}

      {/* Notification config modal */}
      {showNotifModal && (
        <NotifConfigModal lineId={lineId} lineColor={line.color2} txt={txt}
          existing={prefs.lineConfig?.[lineId] || { days:[1,2,3,4,5], hourFrom:7, hourTo:22 }}
          onSave={saveNotifConfig} onClose={() => setShowNotifModal(false)} />
      )}
    </div>
  );
}

function NotifConfigModal({ lineId, lineColor, txt, existing, onSave, onClose }) {
  const DAY_LABELS = ['Dom','Lun','Mar','Mié','Jue','Vie','Sáb'];
  const [days, setDays] = React.useState(existing.days ?? [1,2,3,4,5]);
  const [hourFrom, setHourFrom] = React.useState(existing.hourFrom ?? 6);
  const [hourTo, setHourTo]   = React.useState(existing.hourTo   ?? 23);
  const toggleDay = (d) => setDays(prev => prev.includes(d) ? prev.filter(x=>x!==d) : [...prev,d]);

  return (
    <div style={{ position:'fixed', inset:0, zIndex:300, display:'flex', alignItems:'flex-end', justifyContent:'center', background:'rgba(0,0,0,.45)' }} onClick={onClose}>
      <div onClick={e=>e.stopPropagation()} style={{ background:'var(--surface)', borderRadius:'20px 20px 0 0', width:'100%', maxWidth:420, padding:'20px 20px 36px' }}>
        <div style={{ width:40,height:4,background:'var(--ink-200)',borderRadius:2,margin:'0 auto 16px' }}/>
        <div style={{ fontFamily:'var(--font-display)', fontWeight:800, fontSize:16, marginBottom:4 }}>
          Alertas — Línea {lineId}
        </div>
        <div style={{ fontSize:12, color:'var(--text-muted)', marginBottom:16 }}>
          Recibirás una notificación push cuando haya novedades en esta línea.
        </div>

        {/* Days */}
        <div style={{ fontSize:11, fontFamily:'var(--font-display)', fontWeight:700, color:'var(--text-muted)', textTransform:'uppercase', letterSpacing:'.08em', marginBottom:8 }}>Días</div>
        <div style={{ display:'flex', gap:6, marginBottom:16 }}>
          {[1,2,3,4,5,6,0].map(d => (
            <button key={d} onClick={()=>toggleDay(d)} style={{ flex:1, padding:'8px 0', borderRadius:8,
              background: days.includes(d) ? lineColor : 'var(--ink-100)',
              color: days.includes(d) ? (lineId==='H'||lineId==='P' ? '#0B1220' : 'white') : 'var(--text-muted)',
              fontFamily:'var(--font-display)', fontWeight:700, fontSize:11 }}>
              {DAY_LABELS[d]}
            </button>
          ))}
        </div>

        {/* Hours */}
        <div style={{ fontSize:11, fontFamily:'var(--font-display)', fontWeight:700, color:'var(--text-muted)', textTransform:'uppercase', letterSpacing:'.08em', marginBottom:8 }}>Horario</div>
        <div style={{ display:'flex', gap:12, alignItems:'center', marginBottom:20 }}>
          <div style={{ flex:1 }}>
            <div style={{ fontSize:11, color:'var(--text-muted)', marginBottom:4 }}>Desde</div>
            <select value={hourFrom} onChange={e=>setHourFrom(+e.target.value)} style={{ width:'100%', padding:'8px 10px', borderRadius:8, border:'1px solid var(--border)', background:'var(--surface)', fontSize:13 }}>
              {Array.from({length:24},(_,i)=>i).map(h=><option key={h} value={h}>{String(h).padStart(2,'0')}:00</option>)}
            </select>
          </div>
          <div style={{ fontSize:18, color:'var(--text-muted)', paddingTop:18 }}>→</div>
          <div style={{ flex:1 }}>
            <div style={{ fontSize:11, color:'var(--text-muted)', marginBottom:4 }}>Hasta</div>
            <select value={hourTo} onChange={e=>setHourTo(+e.target.value)} style={{ width:'100%', padding:'8px 10px', borderRadius:8, border:'1px solid var(--border)', background:'var(--surface)', fontSize:13 }}>
              {Array.from({length:24},(_,i)=>i+1).map(h=><option key={h} value={h}>{String(h).padStart(2,'0')}:00</option>)}
            </select>
          </div>
        </div>

        <button onClick={()=>onSave({days,hourFrom,hourTo})} style={{ width:'100%', padding:'13px', borderRadius:12, background:lineColor, color:(lineId==='H'||lineId==='P')?'#0B1220':'white', fontFamily:'var(--font-display)', fontWeight:800, fontSize:14, border:'none' }}>
          Activar alertas
        </button>
        <button onClick={onClose} style={{ width:'100%', padding:'10px', marginTop:8, borderRadius:12, background:'transparent', color:'var(--text-muted)', fontFamily:'var(--font-display)', fontWeight:700, fontSize:13, border:'none' }}>
          Cancelar
        </button>
      </div>
    </div>
  );
}

// Chip showing next train arrival. Colors stay within the line's own hue palette.
// `lineHex` is the line's dark hex color (e.g. '#0078B1' for A). `darkText` for light lines (H).
function NextTrainChip({ secs, dir, lineHex, darkText = false, faded = false }) {
  const label = secs <= 0   ? '●'
              : secs < 60   ? `${secs}s`
              : secs < 3600 ? `${Math.round(secs / 60)}m`
              : '–';
  // Stay within the line's hue: full color → tinted → very light → neutral bg
  const bg = secs <= 60  ? lineHex
           : secs <= 120 ? `color-mix(in srgb, ${lineHex} 72%, white)`
           : secs <= 300 ? `color-mix(in srgb, ${lineHex} 32%, white)`
           : 'var(--ink-100)';
  // White text on dark/saturated bg; line color on tint; muted on neutral
  const fg = secs <= 120  ? (darkText ? '#0B1220' : 'white')
           : secs <= 300  ? lineHex
           : 'var(--text-muted)';
  const bd = secs <= 300 ? bg : 'var(--border)';
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 2,
      padding: '2px 6px', borderRadius: 6,
      background: bg, color: fg, opacity: faded ? 0.65 : 1,
      fontSize: 10, fontFamily: 'var(--font-display)', fontWeight: 700,
      border: `1px solid ${bd}`,
      transition: 'background .4s, color .4s',
      width: 42, justifyContent: 'center',
    }}>
      <span style={{ fontSize: 8, marginRight: 1 }}>{dir}</span>{label}
    </div>
  );
}

// Small animated train on a track
function TrainIcon({ pos, stationCount, left, color, dir, opacity = 1 }) {
  return (
    <div style={{
      position: 'absolute',
      left: left - 3,
      width: 12,
      height: 20,
      borderRadius: 4,
      background: color,
      border: '2px solid white',
      boxShadow: '0 2px 8px rgba(0,0,0,.2)',
      top: `calc(${(pos / (stationCount - 1)) * 100}% - 10px)`,
      transition: 'top .06s linear',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      zIndex: 3,
      opacity,
    }}>
      <div style={{ width: 4, height: 4, borderRadius: 2, background: '#FFE187' }} />
    </div>
  );
}

Object.assign(window, { ScreenLineDetail });
