// Telehealth flow — office hours + direct 1:1 support
// Self-contained: handles its own internal step state, exits via onExit.

const SLOTS = [
  { day: 'Mon', date: '28 Apr', times: ['09:00', '11:30', '14:00', '16:30'] },
  { day: 'Tue', date: '29 Apr', times: ['08:30', '10:00', '13:00', '15:30', '17:00'] },
  { day: 'Wed', date: '30 Apr', times: ['09:00', '11:00', '14:30'] },
  { day: 'Thu', date: '01 May', times: ['10:30', '13:00', '15:00', '16:00', '18:00'] },
  { day: 'Fri', date: '02 May', times: ['09:30', '12:00', '14:00'] },
];

const PROVIDERS = [
  { id: 'auto', name: 'Direct support', sub: 'The right person can pick it up', avatar: null, role: '' },
  { id: 'mendez', name: 'Iris Mendez, NP', sub: 'Hormones, menopause, recovery', avatar: '👩🏽‍⚕️', role: 'Nurse-Practitioner · FL' },
  { id: 'okafor', name: 'Daniel Okafor, NP', sub: 'Skin, cognition, energy', avatar: '👨🏿‍⚕️', role: 'Nurse-Practitioner · FL' },
  { id: 'park', name: 'Lena Park, NP', sub: 'Sleep, stress, GLP-1 transitions', avatar: '👩🏻‍⚕️', role: 'Nurse-Practitioner · CA' },
];

const Telehealth = ({ contact, track, onExit }) => {
  const cleanPath = new URLSearchParams(location.search).get('path') || '';
  const page = cleanPath.includes('/private-call') ? 'private' : 'office';
  const fname = (contact?.legalName || 'Member').split(' ')[0];

  return (
    <TelehealthShell onExit={onExit}>
      {page === 'private' ? (
        <PrivateCallPage contact={contact} track={track} onExit={onExit} />
      ) : (
        <OfficeHoursStep fname={fname} />
      )}
    </TelehealthShell>
  );
};

const TelehealthShell = ({ children, onExit }) => (
  <div className="th-shell">
    <nav className="nav">
      <div className="nav-inner">
        <Logo size={20} asLink />
        <div className="mono" style={{ fontSize: 11, letterSpacing: '0.15em', color: 'var(--ink-mute)', textTransform: 'uppercase' }}>
          PROVIDER ON CALL
        </div>
        <div className="nav-cta">
          <button className="btn btn-soft btn-sm" onClick={onExit}>← Back to dashboard</button>
        </div>
      </div>
    </nav>
    {children}
  </div>
);

const PrivateCallPage = ({ contact, track, onExit }) => {
  const [step, setStep] = React.useState('support'); // support | in-call | summary
  const [access, setAccess] = React.useState({ loading: true, allowed: false, room: null });
  const [provider, setProvider] = React.useState('auto');
  const [mode, setMode] = React.useState('video'); // video | phone
  const [topic, setTopic] = React.useState('plan');
  const [notes, setNotes] = React.useState('');

  const fname = (contact?.legalName || 'Member').split(' ')[0];
  const trackLabel = (track || 'energy').toLowerCase();
  const providerObj = PROVIDERS.find(p => p.id === provider) || PROVIDERS[0];
  const openOfficeHours = () => {
    if (window.parent && window.parent !== window) {
      window.parent.postMessage({ type: 'axo:site-path', path: '/office-hours' }, window.location.origin);
    } else {
      window.history.pushState(null, '', '/office-hours');
    }
  };

  React.useEffect(() => {
    let alive = true;
    fetch('/api/provider-rooms/current?type=one_on_one')
      .then(res => res.ok ? res.json() : null)
      .then(body => {
        if (!alive) return;
        setAccess({
          loading: false,
          allowed: Boolean(body?.has_private_access || body?.role === 'host'),
          room: body?.room || null,
        });
      })
      .catch(() => {
        if (alive) setAccess({ loading: false, allowed: false, room: null });
      });
    return () => { alive = false; };
  }, []);

  if (access.loading) {
    return (
      <div className="th-stage">
        <div className="th-card">
          <div className="th-eyebrow mono">— PRIVATE CALL</div>
          <h1 className="serif th-h1">Checking access.</h1>
        </div>
      </div>
    );
  }

  if (!access.allowed) {
    return (
      <div className="th-stage">
        <div className="th-card">
          <div className="th-eyebrow mono">— PRIVATE CALL</div>
          <h1 className="serif th-h1">1:1 access is required.</h1>
          <p className="th-sub">Private provider calls are included with the $299 provider access tier. Provider care rooms stay available through the monthly and twice-monthly plans.</p>
          <div className="th-actions">
            <button className="btn btn-soft" onClick={openOfficeHours}>Open office hours</button>
            <button className="btn btn-primary" onClick={onExit}>Back to dashboard</button>
          </div>
        </div>
      </div>
    );
  }

  return (
    <>
      {step === 'support' && (
        <DirectSupportStep
          fname={fname}
          trackLabel={trackLabel}
          provider={provider}
          setProvider={setProvider}
          mode={mode}
          setMode={setMode}
          topic={topic}
          setTopic={setTopic}
          notes={notes}
          setNotes={setNotes}
          onOpen={() => setStep('in-call')}
        />
      )}

      {step === 'in-call' && (
        <InCallStep
          fname={fname}
          provider={providerObj}
          mode={mode}
          topic={topic}
          notes={notes}
          room={access.room}
          onEnd={() => setStep('summary')}
        />
      )}

      {step === 'summary' && (
        <SummaryStep
          fname={fname}
          provider={providerObj}
          mode={mode}
          slot={null}
          topic={topic}
          onDone={onExit}
        />
      )}
    </>
  );
};

const OFFICE_SESSIONS = [
  { label: 'Tuesday', time: '7:00 PM', tz: 'ET' },
  { label: 'Thursday', time: '7:00 PM', tz: 'ET' },
];

const ROOM_PANEL = [
  { name: 'Iris Mendez, NP', role: 'Host provider', status: 'Live' },
  { name: 'Daniel Okafor, NP', role: 'Medication review', status: 'Panel' },
  { name: 'Lena Park, NP', role: 'Follow-up routing', status: 'Panel' },
];

const CallIcon = ({ type }) => {
  if (type === 'mic') return (
    <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3a3 3 0 0 0-3 3v6a3 3 0 0 0 6 0V6a3 3 0 0 0-3-3Z" /><path d="M5 11a7 7 0 0 0 14 0" /><path d="M12 18v3" /><path d="M8 21h8" /></svg>
  );
  if (type === 'video') return (
    <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2Z" /><path d="m16 11 5-3v8l-5-3" /></svg>
  );
  if (type === 'message') return (
    <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5h16v11H8l-4 4SiteZ" /></svg>
  );
  if (type === 'record') return (
    <svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="6" /></svg>
  );
  if (type === 'screen') return (
    <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5h16v11H4z" /><path d="M9 21h6" /><path d="M12 16v5" /></svg>
  );
  if (type === 'controls') return (
    <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h10" /><path d="M18 7h2" /><path d="M16 5v4" /><path d="M4 17h2" /><path d="M10 17h10" /><path d="M8 15v4" /></svg>
  );
  return (
    <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12" /><path d="M18 6 6 18" /></svg>
  );
};

const OfficeHoursStep = ({ fname }) => {
  const [roomStatus, setRoomStatus] = React.useState({ loading: true });
  const [question, setQuestion] = React.useState('');
  const [state, setState] = React.useState('idle');
  const [handRaised, setHandRaised] = React.useState(false);
  const [sharedFile, setSharedFile] = React.useState('');
  const [notesState, setNotesState] = React.useState('idle');
  const [panelEnabled, setPanelEnabled] = React.useState(false);
  const [recording, setRecording] = React.useState(false);
  const [micOn, setMicOn] = React.useState(true);
  const [camOn, setCamOn] = React.useState(false);
  const [screenSharing, setScreenSharing] = React.useState(false);
  const [messagesOpen, setMessagesOpen] = React.useState(false);
  const [hostToolsOpen, setHostToolsOpen] = React.useState(false);
  const [liveStatus, setLiveStatus] = React.useState('connecting');
  const [liveError, setLiveError] = React.useState('');
  const [peers, setPeers] = React.useState([]);
  const [participantCount, setParticipantCount] = React.useState(0);
  const [sideTab, setSideTab] = React.useState('chat');
  const [scheduleEditing, setScheduleEditing] = React.useState(false);
  const [scheduleState, setScheduleState] = React.useState('idle');
  const [schedule, setSchedule] = React.useState(OFFICE_SESSIONS);
  const [chatInput, setChatInput] = React.useState('');
  const [chatMessages, setChatMessages] = React.useState([]);
  const [questions, setQuestions] = React.useState([]);
  const screenStreamRef = React.useRef(null);
  const transcriptSyncRef = React.useRef(null);
  const wsRef = React.useRef(null);
  const localStreamRef = React.useRef(null);
  const peerConnectionsRef = React.useRef({});
  const remoteAudioRef = React.useRef({});
  const remoteStreamsRef = React.useRef({});
  const mediaRecorderRef = React.useRef(null);
  const recordingChunksRef = React.useRef([]);
  const recordingStartedAtRef = React.useRef(0);
  const [transcriptLines, setTranscriptLines] = React.useState([]);
  const roomRole = roomStatus?.role || window.AXO_ROOM_ROLE || 'member';
  const canManageRoom = ['host', 'admin', 'moderator', 'panelist'].includes(roomRole);
  const canEditSchedule = Boolean(roomStatus?.can_edit_schedule) || Boolean(window.AXO_CAN_EDIT_SCHEDULE);
  React.useEffect(() => {
    let alive = true;
    fetch('/api/provider-rooms/current?type=office_hours')
      .then(res => res.ok ? res.json() : null)
      .then(body => {
        if (!alive) return;
        setRoomStatus(body || { loading: false, has_access: false });
        const savedSchedule = body?.room?.config?.office_hours_schedule;
        if (Array.isArray(savedSchedule) && savedSchedule.length > 0) {
          setSchedule(savedSchedule.map((item) => ({
            label: item.label || '',
            time: item.time || '',
            tz: item.tz || 'ET',
          })));
        }
        if (body?.my_question?.body) {
          setQuestion(body.my_question.body);
          setQuestions([{ id: body.my_question.id || 'saved-question', topic: body.my_question.body, answer: body.my_question.answer || '' }]);
        }
        if (body?.room?.id && body?.has_access) {
          fetch(`/api/provider-rooms/${body.room.id}/questions`)
            .then((res) => res.ok ? res.json() : null)
            .then((questionBody) => {
              if (!alive || !Array.isArray(questionBody?.questions)) return;
              setQuestions(questionBody.questions.map((item, index) => ({
                id: item.id || `question-${index}`,
                topic: item.body || item.topic || '',
                answer: item.answer_text || '',
                name: item.member_name || item.public_name || item.name || '',
              })).filter(item => item.topic));
            })
            .catch(() => {});
        }
      })
      .catch(() => {
        if (alive) setRoomStatus({ loading: false, has_access: false });
      });
    return () => { alive = false; };
  }, []);

  React.useEffect(() => {
    if (!roomStatus?.room) return undefined;
    if (!roomStatus.has_access) {
      setLiveStatus('blocked');
      setLiveError('Member access is required for this room.');
      return undefined;
    }

    let closed = false;
    setLiveStatus('connecting');
    fetch(`/api/provider-rooms/${roomStatus.room.id}/live-token`, { method: 'POST' })
      .then(res => {
        if (!res.ok) throw new Error('room_token_failed');
        return res.json();
      })
      .then(body => {
        if (closed || !body.websocket_url) return;
        openCareRoomSocket(body.websocket_url);
      })
      .catch(() => {
        if (!closed) {
          setLiveStatus('error');
          setLiveError('Unable to open the care room.');
        }
      });

    return () => {
      closed = true;
      closeLiveRoom();
    };
  }, [roomStatus?.room?.id, roomStatus?.has_access]);

  const submitQuestion = () => {
    if (question.trim().length < 4) return;
    setState('saving');
    const body = question.trim();
    const localQuestion = { id: `question-${Date.now()}`, topic: body, answer: '' };
    const addLocalQuestion = () => {
      setQuestions((items) => [...items, localQuestion]);
      setQuestion('');
      setState('saved');
    };
    if (!roomStatus?.room || !roomStatus?.has_access) {
      setState('error');
      return;
    }
    fetch(`/api/provider-rooms/${roomStatus.room.id}/questions`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ body, anonymous: true }),
    })
      .then(res => {
        if (!res.ok) throw new Error('question_failed');
        addLocalQuestion();
        sendRoomMessage({ type: 'question', question: { topic: body } });
      })
      .catch(() => setState('error'));
  };

  const saveCareNotes = (lines = transcriptLines, nextRecording = recording) => {
    if (!roomStatus?.room) return;
    setNotesState('saving');
    fetch(`/api/provider-rooms/${roomStatus.room.id}/ai-notes`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        title: 'Care room transcript',
        transcript: lines.map((line) => `[${line.time}] ${line.speaker}: ${line.text}`).join('\n'),
        room_state: {
          channel: 'care_room',
          providers: panelEnabled ? ROOM_PANEL.map(p => p.name) : ['AmazingXO Provider'],
          questions,
          schedule,
          chat: chatMessages,
          transcript: lines,
          shared_file: sharedFile || null,
          hand_raised: handRaised,
          recording: nextRecording,
        },
      }),
    })
      .then(res => {
        if (!res.ok) throw new Error('notes_failed');
        setNotesState('saved');
      })
      .catch(() => setNotesState('error'));
  };

  const updateSchedule = (index, field, value) => {
    setSchedule(schedule.map((item, i) => (
      i === index ? { ...item, [field]: value } : item
    )));
  };

  const toggleScheduleEditing = () => {
    if (!scheduleEditing) {
      setScheduleEditing(true);
      setScheduleState('idle');
      return;
    }
    if (!roomStatus?.room) {
      setScheduleEditing(false);
      return;
    }
    setScheduleState('saving');
    fetch(`/api/provider-rooms/${roomStatus.room.id}/schedule`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ schedule }),
    })
      .then(res => {
        if (!res.ok) throw new Error('schedule_failed');
        return res.json();
      })
      .then(body => {
        if (Array.isArray(body.schedule)) setSchedule(body.schedule);
        setScheduleEditing(false);
        setScheduleState('saved');
      })
      .catch(() => setScheduleState('error'));
  };

  const sendChat = () => {
    const text = chatInput.trim();
    if (!text) return;
    sendRoomMessage({ type: 'chat', text });
    setChatInput('');
  };

  const toggleRecording = async () => {
    const nextRecording = !recording;
    if (nextRecording) {
      await startRoomRecording();
    } else {
      stopRoomRecording();
    }
    setRecording(nextRecording);
    sendRoomMessage({ type: 'recording', active: nextRecording });
    if (recording) saveCareNotes(transcriptLines, nextRecording);
  };

  const startRoomRecording = async () => {
    if (!canManageRoom || mediaRecorderRef.current || typeof MediaRecorder === 'undefined') return;
    const localStream = await startLocalMedia({ audio: true, video: camOn }).catch(() => null);
    const stream = new MediaStream();
    localStream?.getAudioTracks?.().forEach((track) => stream.addTrack(track));
    Object.values(remoteStreamsRef.current).forEach((remoteStream) => {
      remoteStream?.getAudioTracks?.().forEach((track) => stream.addTrack(track));
    });
    if (stream.getTracks().length === 0) return;

    const mimeType = MediaRecorder.isTypeSupported?.('audio/webm;codecs=opus')
      ? 'audio/webm;codecs=opus'
      : 'audio/webm';
    const recorder = new MediaRecorder(stream, { mimeType });
    recordingChunksRef.current = [];
    recordingStartedAtRef.current = Date.now();
    recorder.ondataavailable = (event) => {
      if (event.data && event.data.size > 0) recordingChunksRef.current.push(event.data);
    };
    recorder.onstop = () => uploadRoomRecording(mimeType);
    mediaRecorderRef.current = recorder;
    recorder.start(2000);
  };

  const stopRoomRecording = () => {
    if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') {
      mediaRecorderRef.current.stop();
    }
    mediaRecorderRef.current = null;
  };

  const uploadRoomRecording = async (mimeType) => {
    if (!roomStatus?.room || recordingChunksRef.current.length === 0) return;
    const blob = new Blob(recordingChunksRef.current, { type: mimeType });
    recordingChunksRef.current = [];
    const audio_base64 = await blobToBase64(blob);
    fetch(`/api/provider-rooms/${roomStatus.room.id}/recording`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        audio_base64,
        mime_type: mimeType,
        duration_ms: Math.max(0, Date.now() - recordingStartedAtRef.current),
        transcript: transcriptLines.map((line) => `[${line.time}] ${line.speaker}: ${line.text}`).join('\n'),
      }),
    }).catch(() => {});
  };

  const blobToBase64 = (blob) => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(String(reader.result || '').split(',').pop() || '');
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });

  const openCareRoomSocket = (url) => {
    closeLiveRoom();
    const ws = new WebSocket(url);
    wsRef.current = ws;
    ws.onopen = () => setLiveStatus('live');
    ws.onclose = () => setLiveStatus((status) => status === 'blocked' ? status : 'closed');
    ws.onerror = () => {
      setLiveStatus('error');
      setLiveError('Care room connection failed.');
    };
    ws.onmessage = (event) => {
      let msg;
      try { msg = JSON.parse(event.data); } catch { return; }
      handleRoomMessage(msg);
    };
  };

  const closeLiveRoom = () => {
    stopRoomRecording();
    Object.values(peerConnectionsRef.current).forEach((pc) => pc.close?.());
    Object.values(remoteAudioRef.current).forEach((audio) => {
      audio.pause?.();
      audio.srcObject = null;
    });
    peerConnectionsRef.current = {};
    remoteAudioRef.current = {};
    remoteStreamsRef.current = {};
    wsRef.current?.close?.();
    wsRef.current = null;
  };

  const sendRoomMessage = (payload) => {
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify(payload));
    }
  };

  const handleRoomMessage = (msg) => {
    if (msg.type === 'ready' || msg.type === 'presence') {
      setParticipantCount(msg.participant_count || 0);
      setPeers(msg.peers || []);
      (msg.peers || []).forEach((peer) => {
        if (roomRole === 'host' && !peerConnectionsRef.current[peer.id]) {
          ensurePeerConnection(peer.id, true);
        }
      });
      return;
    }
    if (msg.type === 'peer-joined') {
      setParticipantCount(msg.participant_count || 0);
      setPeers((items) => [...items.filter((p) => p.id !== msg.peer.id), msg.peer]);
      if (roomRole === 'host') ensurePeerConnection(msg.peer.id, true);
      return;
    }
    if (msg.type === 'peer-left') {
      setParticipantCount(msg.participant_count || 0);
      removePeer(msg.id);
      return;
    }
    if (msg.type === 'signal') {
      handleSignal(msg.from, msg.data);
      return;
    }
    if (msg.type === 'chat') {
      setChatMessages((items) => [...items, { from: msg.name || 'Room', text: msg.text }]);
      return;
    }
    if (msg.type === 'question') {
      const topic = typeof msg.question?.topic === 'string' ? msg.question.topic : msg.question?.body || '';
      if (topic) {
        setQuestions((items) => [
          ...items,
          { id: `live-question-${msg.created_at || Date.now()}`, topic, answer: '', name: msg.name || 'Member' },
        ]);
      }
      return;
    }
    if (msg.type === 'speaker-request' && canManageRoom) {
      setPeers((items) => items.map((p) => p.id === msg.from ? { ...p, requested: true, name: msg.name || p.name } : p));
      setHostToolsOpen(true);
      return;
    }
    if (msg.type === 'speaker-approved') {
      setHandRaised(false);
      setMicOn(true);
      startLocalMedia({ audio: true, video: camOn });
      return;
    }
    if (msg.type === 'force-mute') {
      setMicOn(false);
      localStreamRef.current?.getAudioTracks?.().forEach((track) => { track.enabled = false; });
      return;
    }
    if (msg.type === 'removed') {
      closeLiveRoom();
      setLiveStatus('blocked');
      setLiveError('You have been removed from this room.');
      return;
    }
    if (msg.type === 'recording') {
      setRecording(Boolean(msg.active));
    }
  };

  const ensurePeerConnection = async (peerId, initiator = false) => {
    if (peerConnectionsRef.current[peerId]) return peerConnectionsRef.current[peerId];
    const pc = new RTCPeerConnection({
      iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' }],
    });
    peerConnectionsRef.current[peerId] = pc;

    const stream = await startLocalMedia({ audio: canManageRoom ? micOn : false, video: camOn });
    stream?.getTracks?.().forEach((track) => pc.addTrack(track, stream));

    pc.onicecandidate = (event) => {
      if (event.candidate) {
        sendRoomMessage({ type: 'signal', to: peerId, data: { type: 'candidate', candidate: event.candidate } });
      }
    };
    pc.ontrack = (event) => {
      let audio = remoteAudioRef.current[peerId];
      if (!audio) {
        audio = new Audio();
        audio.autoplay = true;
        audio.playsInline = true;
        remoteAudioRef.current[peerId] = audio;
      }
      audio.srcObject = event.streams[0];
      remoteStreamsRef.current[peerId] = event.streams[0];
      audio.play?.().catch(() => {});
    };

    if (initiator) {
      const offer = await pc.createOffer();
      await pc.setLocalDescription(offer);
      sendRoomMessage({ type: 'signal', to: peerId, data: { type: 'offer', sdp: offer } });
    }
    return pc;
  };

  const handleSignal = async (peerId, data) => {
    const pc = await ensurePeerConnection(peerId, false);
    if (data.type === 'offer') {
      await pc.setRemoteDescription(new RTCSessionDescription(data.sdp));
      const answer = await pc.createAnswer();
      await pc.setLocalDescription(answer);
      sendRoomMessage({ type: 'signal', to: peerId, data: { type: 'answer', sdp: answer } });
    } else if (data.type === 'answer') {
      await pc.setRemoteDescription(new RTCSessionDescription(data.sdp));
    } else if (data.type === 'candidate' && data.candidate) {
      await pc.addIceCandidate(new RTCIceCandidate(data.candidate)).catch(() => {});
    }
  };

  const removePeer = (peerId) => {
    peerConnectionsRef.current[peerId]?.close?.();
    delete peerConnectionsRef.current[peerId];
    if (remoteAudioRef.current[peerId]) {
      remoteAudioRef.current[peerId].pause?.();
      remoteAudioRef.current[peerId].srcObject = null;
      delete remoteAudioRef.current[peerId];
    }
    delete remoteStreamsRef.current[peerId];
    setPeers((items) => items.filter((p) => p.id !== peerId));
  };

  const startLocalMedia = async ({ audio, video }) => {
    if (!audio && !video) return localStreamRef.current;
    if (!navigator.mediaDevices?.getUserMedia) return null;
    if (!localStreamRef.current) {
      localStreamRef.current = await navigator.mediaDevices.getUserMedia({ audio: Boolean(audio), video: Boolean(video) }).catch(async () => {
        if (video) return navigator.mediaDevices.getUserMedia({ audio: Boolean(audio), video: false });
        throw new Error('media_unavailable');
      });
    }
    localStreamRef.current?.getAudioTracks?.().forEach((track) => { track.enabled = Boolean(audio); });
    localStreamRef.current?.getVideoTracks?.().forEach((track) => { track.enabled = Boolean(video); });
    return localStreamRef.current;
  };

  const toggleMic = async () => {
    const next = !micOn;
    setMicOn(next);
    await startLocalMedia({ audio: next, video: camOn });
    localStreamRef.current?.getAudioTracks?.().forEach((track) => { track.enabled = next; });
    sendRoomMessage({ type: 'self-muted', active: !next });
  };

  const toggleCamera = async () => {
    const next = !camOn;
    setCamOn(next);
    await startLocalMedia({ audio: micOn, video: next });
    localStreamRef.current?.getVideoTracks?.().forEach((track) => { track.enabled = next; });
  };

  const requestToSpeak = () => {
    const next = !handRaised;
    setHandRaised(next);
    if (next) sendRoomMessage({ type: 'request-speak' });
  };

  const approveSpeaker = (peerId) => {
    sendRoomMessage({ type: 'approve-speaker', participantId: peerId });
    setPeers((items) => items.map((p) => p.id === peerId ? { ...p, requested: false, speaking: true, muted: false } : p));
  };

  const muteParticipant = (peerId) => {
    sendRoomMessage({ type: 'mute-participant', participantId: peerId });
    setPeers((items) => items.map((p) => p.id === peerId ? { ...p, muted: true } : p));
  };

  const removeParticipant = (peerId) => {
    sendRoomMessage({ type: 'kick-participant', participantId: peerId });
    removePeer(peerId);
  };

  const toggleScreenShare = async () => {
    if (screenSharing) {
      screenStreamRef.current?.getTracks?.().forEach((track) => track.stop());
      screenStreamRef.current = null;
      setScreenSharing(false);
      return;
    }

    try {
      if (navigator.mediaDevices?.getDisplayMedia) {
        const stream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false });
        screenStreamRef.current = stream;
        stream.getVideoTracks?.()[0]?.addEventListener('ended', () => {
          screenStreamRef.current = null;
          setScreenSharing(false);
        });
      }
      setScreenSharing(true);
    } catch {
      return;
    }
  };

  const answerQuestion = (id) => {
    const item = questions.find((q) => q.id === id);
    if (!item) return;
    setQuestions((items) => items.map((q) => (
      q.id === id ? { ...q, answer: 'Preparing...' } : q
    )));
    fetch(`/api/provider-rooms/${roomStatus.room.id}/live-intelligence`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        question: item.topic,
        transcript: transcriptLines.map((line) => `${line.speaker}: ${line.text}`).join('\n'),
        context: { schedule, recording, participant_count: participantCount },
      }),
    })
      .then(res => {
        if (!res.ok) throw new Error('answer_failed');
        return res.json();
      })
      .then(body => {
        setQuestions((items) => items.map((q) => (
          q.id === id ? { ...q, answer: body.suggested_answer || 'Review this question live.' } : q
        )));
      })
      .catch(() => {
        setQuestions((items) => items.map((q) => (
          q.id === id ? { ...q, answer: 'Review this question live.' } : q
        )));
      });
  };

  React.useEffect(() => {
    if (!['chat', 'questions', 'transcript'].includes(sideTab)) {
      setSideTab('chat');
    }
  }, [sideTab]);

  React.useEffect(() => {
    if (!roomStatus?.room || transcriptLines.length === 0) return undefined;
    if (transcriptSyncRef.current) window.clearTimeout(transcriptSyncRef.current);
    transcriptSyncRef.current = window.setTimeout(() => {
      saveCareNotes(transcriptLines, recording);
    }, 1200);
    return () => {
      if (transcriptSyncRef.current) window.clearTimeout(transcriptSyncRef.current);
    };
  }, [roomStatus?.room, transcriptLines, recording]);

  if (roomStatus?.loading) {
    return (
      <div className="th-room">
        <div className="th-room-access">
          <div className="th-eyebrow mono">— LIVE CARE ROOM</div>
          <h1 className="serif th-room-title">Checking member access.</h1>
        </div>
      </div>
    );
  }

  if (!roomStatus?.has_access) {
    return (
      <div className="th-room">
        <div className="th-room-access">
          <div className="th-eyebrow mono">— LIVE CARE ROOM</div>
          <h1 className="serif th-room-title">Member access is required.</h1>
        </div>
      </div>
    );
  }

  return (
    <div className="th-room">
      <div className="th-room-top">
        <div>
          <div className="th-eyebrow mono">— PROVIDER ON CALL</div>
          <h1 className="serif th-room-title">Live care room.</h1>
        </div>
        <div className="th-room-status">
          <span className="th-live-dot" />
          <div className="th-room-schedule">
            {schedule.map((s, i) => (
              <div className="th-room-session" key={`${s.label}-${i}`}>
                {scheduleEditing ? (
                  <>
                    <input value={s.label} onChange={(e) => updateSchedule(i, 'label', e.target.value)} />
                    <input value={s.time} onChange={(e) => updateSchedule(i, 'time', e.target.value)} />
                  </>
                ) : (
                  <>
                    <strong>{s.label}</strong>
                    <span className="mono">{s.time}</span>
                    <em className="mono">{s.tz}</em>
                  </>
                )}
              </div>
            ))}
          </div>
          {canEditSchedule && (
            <button className="th-mini-toggle mono" onClick={toggleScheduleEditing} disabled={scheduleState === 'saving'}>
              {scheduleState === 'saving' ? 'SAVING' : scheduleEditing ? 'DONE' : 'EDIT'}
            </button>
          )}
        </div>
      </div>

      <div className="th-room-grid">
        <main className="th-room-main">
          <section className="th-stage-video" onClick={() => { if (hostToolsOpen) setHostToolsOpen(false); }}>
            <button
              className={`th-recording-pill mono ${recording ? 'is-active' : ''}`}
              onClick={(e) => {
                e.stopPropagation();
                toggleRecording();
              }}
              aria-pressed={recording}
              aria-label={recording ? 'Stop recording' : 'Start recording'}
              title={recording ? 'Stop recording' : 'Start recording'}
            >
              <span className={recording ? 'is-recording' : ''} />
              {recording ? 'Stop recording' : 'Start recording'}
            </button>

            <div className="th-stage-provider">
              <div className="th-stage-avatar">AXO</div>
              <div>
                <div className="th-stage-name serif">AmazingXO</div>
              </div>
            </div>

            <div className="th-stage-member">
              <div className="th-stage-member-avatar">{fname[0]}</div>
              <div className="mono">YOU · LISTENING</div>
            </div>

            <div className="th-video-controls" onClick={(e) => e.stopPropagation()}>
              <button className={micOn ? '' : 'is-muted'} onClick={toggleMic} title={micOn ? 'Mute' : 'Unmute'} aria-label={micOn ? 'Mute' : 'Unmute'}>
                <CallIcon type="mic" />
              </button>
              <button className={camOn ? 'is-on' : ''} onClick={toggleCamera} title={camOn ? 'Turn video off' : 'Turn video on'} aria-label={camOn ? 'Turn video off' : 'Turn video on'}>
                <CallIcon type="video" />
              </button>
              <button className={messagesOpen ? 'is-on' : ''} onClick={() => setMessagesOpen(!messagesOpen)} title="Messages" aria-label="Messages">
                <CallIcon type="message" />
              </button>
              {canManageRoom && (
                <button className={screenSharing ? 'is-on' : ''} onClick={toggleScreenShare} title={screenSharing ? 'Stop sharing' : 'Share screen'} aria-label={screenSharing ? 'Stop sharing' : 'Share screen'}>
                  <CallIcon type="screen" />
                </button>
              )}
              {canManageRoom && (
                <button className={hostToolsOpen ? 'is-on' : ''} onClick={() => setHostToolsOpen(!hostToolsOpen)} title="Host tools" aria-label="Host tools">
                  <CallIcon type="controls" />
                </button>
              )}
              <button className="is-end" title="Leave room" aria-label="Leave room">
                <CallIcon type="end" />
              </button>
            </div>

            {canManageRoom && hostToolsOpen && (
              <div className="th-host-drawer" onClick={(e) => e.stopPropagation()}>
                <div className="th-host-drawer-head">
                  <strong>Host tools</strong>
                  <button onClick={() => setHostToolsOpen(false)} aria-label="Close host tools">×</button>
                </div>
                <button onClick={toggleRecording}>{recording ? 'Stop recording' : 'Start recording'}</button>
                <button onClick={toggleScreenShare}>{screenSharing ? 'Stop sharing' : 'Share screen'}</button>
                <button onClick={() => setPanelEnabled(!panelEnabled)}>{panelEnabled ? 'Panel on' : 'Panel off'}</button>
                <div className="th-host-peers">
                  {peers.map((peer) => (
                    <div key={peer.id} className="th-host-peer">
                      <span>{peer.name}</span>
                      <em>{peer.requested ? 'Requesting' : peer.speaking ? 'Speaking' : 'Listening'}</em>
                      {peer.requested && <button onClick={() => approveSpeaker(peer.id)}>Approve</button>}
                      <button onClick={() => muteParticipant(peer.id)}>Mute</button>
                      <button onClick={() => removeParticipant(peer.id)}>Remove</button>
                    </div>
                  ))}
                </div>
              </div>
            )}
          </section>

          <div className="th-room-controls">
            <button
              className={`th-room-control is-primary ${handRaised ? 'is-active' : ''}`}
              onClick={requestToSpeak}
            >
              <span>{handRaised ? 'Hand raised' : 'Request to speak'}</span>
              <em className="mono">{handRaised ? 'REQUEST SENT' : 'OPTIONAL'}</em>
            </button>
            <label className="th-room-control">
              <input
                type="file"
                onChange={(e) => setSharedFile(e.target.files?.[0]?.name || '')}
              />
              <span>{sharedFile || 'Share lab / Rx'}</span>
              <em className="mono">FOR PROVIDER REVIEW</em>
            </label>
          </div>

          <section className="th-room-compose">
            <div>
              <div className="th-h mono">— QUESTIONS</div>
              <h2 className="serif">Share your question...</h2>
            </div>
            <textarea
              className="th-notes th-office-question"
              value={question}
              maxLength={1200}
              rows={7}
              placeholder="Write the question or context you want the provider to address..."
              onChange={(e) => {
                setQuestion(e.target.value);
                if (state !== 'idle') setState('idle');
              }}
            />
            <div className="th-actions">
              <button
                className="btn btn-primary th-send-question"
                onClick={submitQuestion}
                disabled={question.trim().length < 4 || state === 'saving'}
                style={{ opacity: question.trim().length >= 4 ? 1 : 0.45 }}
              >
                {state === 'saving' ? 'Saving...' : state === 'saved' ? 'Saved' : 'Send question'}
              </button>
            </div>
            {state === 'error' && <p className="th-office-error">That question did not save. Try again.</p>}
          </section>

        </main>

        <aside className="th-room-side">
          <section className="th-room-panel th-tabs-panel">
            <div className="th-side-tabs">
              {[
                ['chat', 'Chat'],
                ['questions', 'Questions'],
                ['transcript', 'Transcript'],
              ].map(([id, label]) => (
                <button key={id} className={sideTab === id ? 'is-active' : ''} onClick={() => setSideTab(id)}>
                  {label}
                </button>
              ))}
            </div>

            {sideTab === 'questions' && (
              <div className="th-tab-body">
                <div className="th-h mono">— QUESTIONS</div>
                <div className="th-live-questions">
                  {questions.map((item, i) => (
                    <div key={item.id} className="th-question-item">
                      <div className="mono">{String(i + 1).padStart(2, '0')}</div>
                      <div>
                        <strong>{item.topic}</strong>
                        {item.name && <small className="mono">{item.name}</small>}
                        {item.answer && <span>{item.answer}</span>}
                      </div>
                      {canManageRoom && !item.answer && (
                        <button onClick={() => answerQuestion(item.id)}>Answer</button>
                      )}
                    </div>
                  ))}
                  {questions.length === 0 && <div className="th-empty-line">No questions yet.</div>}
                </div>
              </div>
            )}

            {sideTab === 'chat' && (
              <div className="th-tab-body">
                <div className="th-h mono">— ROOM CHAT</div>
                <div className="th-chat-list">
                  {chatMessages.map((message, i) => (
                    <div key={`${message.from}-${i}`} className={`th-chat-message th-chat-${message.from}`}>
                      <strong>{message.from}</strong>
                      <span>{message.text}</span>
                    </div>
                  ))}
                </div>
                <div className="th-chat-compose">
                  <input
                    value={chatInput}
                    onChange={(e) => setChatInput(e.target.value)}
                    onKeyDown={(e) => { if (e.key === 'Enter') sendChat(); }}
                    placeholder="Write to the room..."
                  />
                  <button onClick={sendChat}>Send</button>
                </div>
              </div>
            )}

            {sideTab === 'transcript' && (
              <div className="th-tab-body th-transcript-body">
                <div className="th-transcript-list">
                  {transcriptLines.map((line, i) => (
                    <div key={`${line.time}-${i}`} className="th-transcript-line">
                      <em className="mono">{line.time}</em>
                      <div>
                        <strong>{line.speaker}</strong>
                        <span>{line.text}</span>
                      </div>
                    </div>
                  ))}
                </div>
              </div>
            )}
          </section>
        </aside>
      </div>
    </div>
  );
};

// ─── 1. SCHEDULE ───────────────────────────────────────────────────────────

const TOPICS = [
  { id: 'plan', label: 'Plan adjustment', sub: 'Dose, timing, swap, add a vial' },
  { id: 'side', label: 'Side-effect check', sub: "Something feels off — want to confirm" },
  { id: 'progress', label: 'Progress + next step', sub: 'Where am I, what comes next' },
  { id: 'other', label: 'Something else', sub: 'I\'ll write it below' },
];

const DirectSupportStep = ({ fname, trackLabel, provider, setProvider, mode, setMode, topic, setTopic, notes, setNotes, onOpen }) => {
  return (
    <div className="th-stage">
      <div className="th-card">
        <div className="th-eyebrow mono">— 1:1 ACCESS</div>
        <h1 className="serif th-h1">Direct support, {fname}. <em>When it needs a private room.</em></h1>
        <p className="th-sub">Use this for private support that should not be handled in the provider care room. Start with context, then continue by message, audio, or video when someone from the care team picks it up. This is not for emergencies; if you're in crisis, call 911.</p>

        <div className="th-section">
          <div className="th-h mono">— ROUTING</div>
          <div className="th-providers">
            {PROVIDERS.map(p => (
              <button
                key={p.id}
                className={`th-provider ${provider === p.id ? 'is-active' : ''}`}
                onClick={() => setProvider(p.id)}
              >
                <div className="th-provider-avatar">{p.avatar || '◐'}</div>
                <div>
                  <div className="th-provider-name">{p.name}</div>
                  <div className="th-provider-sub">{p.sub}</div>
                  {p.role && <div className="th-provider-role mono">{p.role}</div>}
                </div>
                {provider === p.id && <div className="th-provider-check">✓</div>}
              </button>
            ))}
          </div>
        </div>

        <div className="th-row">
          <div className="th-section" style={{ flex: 1 }}>
            <div className="th-h mono">— HOW YOU WANT TO START</div>
            <div className="th-mode">
              <button className={`th-mode-pill ${mode === 'message' ? 'is-active' : ''}`} onClick={() => setMode('message')}>
                <span className="th-mode-icon">✎</span> Message
              </button>
              <button className={`th-mode-pill ${mode === 'phone' ? 'is-active' : ''}`} onClick={() => setMode('phone')}>
                <span className="th-mode-icon">☏</span> Audio
              </button>
              <button className={`th-mode-pill ${mode === 'video' ? 'is-active' : ''}`} onClick={() => setMode('video')}>
                <span className="th-mode-icon">▶</span> Video
              </button>
            </div>
          </div>
        </div>

        <div className="th-section">
          <div className="th-h mono">— WHAT'S THIS CALL ABOUT</div>
          <div className="th-topics">
            {TOPICS.map(t => (
              <button
                key={t.id}
                className={`th-topic ${topic === t.id ? 'is-active' : ''}`}
                onClick={() => setTopic(t.id)}
              >
                <div className="th-topic-name">{t.label}</div>
                <div className="th-topic-sub">{t.sub}</div>
              </button>
            ))}
          </div>
          <textarea
            className="th-notes"
            placeholder={`A few words for ${PROVIDERS.find(p => p.id === provider)?.name?.split(',')[0] || 'the care team'} — what should they know first?`}
            value={notes}
            onChange={(e) => setNotes(e.target.value)}
            rows={3}
          />
          <div className="th-context mono">
            — YOUR CATEGORY: {trackLabel.toUpperCase()} · THIS STAYS ON YOUR MEMBER RECORD
          </div>
        </div>

        <div className="th-actions">
          <div className="th-summary mono">
            PROVIDER CARE ROOMS PLUS PRIVATE 1:1 ACCESS
          </div>
          <button
            className="btn btn-primary btn-lg"
            onClick={onOpen}
          >
            Open 1:1 support →
          </button>
        </div>
      </div>
    </div>
  );
};

// ─── 2. DIRECT SUPPORT ROOM ─────────────────────────────────────────────

const InCallStep = ({ fname, provider, mode, topic, notes, room, onEnd }) => {
  const [seconds, setSeconds] = React.useState(0);
  const [muted, setMuted] = React.useState(false);
  const [camOn, setCamOn] = React.useState(mode === 'video');
  const [chatOpen, setChatOpen] = React.useState(mode === 'message');
  const [chatInput, setChatInput] = React.useState('');
  const [liveState, setLiveState] = React.useState('connecting');
  const [chat, setChat] = React.useState([
    { from: 'provider', text: `Hi ${fname} — give me a moment, just pulling up your assessment.`, t: '00:04' },
  ]);
  const wsRef = React.useRef(null);
  const pcRef = React.useRef(null);
  const localStreamRef = React.useRef(null);
  const remoteVideoRef = React.useRef(null);
  const selfVideoRef = React.useRef(null);
  const myIdRef = React.useRef('');
  const topicLabel = TOPICS.find(t => t.id === topic)?.label || 'Plan adjustment';

  React.useEffect(() => {
    const t = setInterval(() => setSeconds(s => s + 1), 1000);
    return () => clearInterval(t);
  }, []);

  React.useEffect(() => {
    if (!room?.id) return undefined;
    let closed = false;
    fetch(`/api/provider-rooms/${room.id}/live-token`, { method: 'POST' })
      .then(res => {
        if (!res.ok) throw new Error('private_token_failed');
        return res.json();
      })
      .then(body => {
        if (closed || !body.websocket_url) return;
        openPrivateSocket(body.websocket_url);
      })
      .catch(() => setLiveState('error'));

    return () => {
      closed = true;
      wsRef.current?.close?.();
      pcRef.current?.close?.();
      localStreamRef.current?.getTracks?.().forEach((track) => track.stop());
    };
  }, [room?.id]);

  React.useEffect(() => {
    localStreamRef.current?.getAudioTracks?.().forEach((track) => { track.enabled = !muted; });
  }, [muted]);

  React.useEffect(() => {
    localStreamRef.current?.getVideoTracks?.().forEach((track) => { track.enabled = camOn; });
  }, [camOn]);

  const openPrivateSocket = (url) => {
    const ws = new WebSocket(url);
    wsRef.current = ws;
    ws.onopen = () => setLiveState('live');
    ws.onclose = () => setLiveState('closed');
    ws.onerror = () => setLiveState('error');
    ws.onmessage = (event) => {
      let msg;
      try { msg = JSON.parse(event.data); } catch { return; }
      if (msg.type === 'ready') {
        myIdRef.current = msg.id || '';
        (msg.peers || []).forEach((peer) => ensurePrivatePeer(peer.id, true));
      }
      if (msg.type === 'peer-joined') ensurePrivatePeer(msg.peer.id, true);
      if (msg.type === 'signal') handlePrivateSignal(msg.from, msg.data);
      if (msg.type === 'chat') setChat((items) => [...items, { from: 'provider', text: msg.text, t: fmt(seconds) }]);
    };
  };

  const sendPrivate = (payload) => {
    if (wsRef.current?.readyState === WebSocket.OPEN) wsRef.current.send(JSON.stringify(payload));
  };

  const ensurePrivatePeer = async (peerId, initiator = false) => {
    if (pcRef.current) return pcRef.current;
    const pc = new RTCPeerConnection({
      iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' }],
    });
    pcRef.current = pc;
    const stream = await startPrivateMedia();
    stream?.getTracks?.().forEach((track) => pc.addTrack(track, stream));
    pc.onicecandidate = (event) => {
      if (event.candidate) sendPrivate({ type: 'signal', to: peerId, data: { type: 'candidate', candidate: event.candidate } });
    };
    pc.ontrack = (event) => {
      if (remoteVideoRef.current) {
        remoteVideoRef.current.srcObject = event.streams[0];
        remoteVideoRef.current.play?.().catch(() => {});
      }
    };
    if (initiator) {
      const offer = await pc.createOffer();
      await pc.setLocalDescription(offer);
      sendPrivate({ type: 'signal', to: peerId, data: { type: 'offer', sdp: offer } });
    }
    return pc;
  };

  const handlePrivateSignal = async (peerId, data) => {
    const pc = await ensurePrivatePeer(peerId, false);
    if (data.type === 'offer') {
      await pc.setRemoteDescription(new RTCSessionDescription(data.sdp));
      const answer = await pc.createAnswer();
      await pc.setLocalDescription(answer);
      sendPrivate({ type: 'signal', to: peerId, data: { type: 'answer', sdp: answer } });
    } else if (data.type === 'answer') {
      await pc.setRemoteDescription(new RTCSessionDescription(data.sdp));
    } else if (data.type === 'candidate' && data.candidate) {
      await pc.addIceCandidate(new RTCIceCandidate(data.candidate)).catch(() => {});
    }
  };

  const startPrivateMedia = async () => {
    if (localStreamRef.current) return localStreamRef.current;
    if (mode === 'message') return null;
    if (!navigator.mediaDevices?.getUserMedia) return null;
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: mode === 'video' }).catch(async () => {
      return navigator.mediaDevices.getUserMedia({ audio: true, video: false });
    });
    stream.getAudioTracks?.().forEach((track) => { track.enabled = !muted; });
    stream.getVideoTracks?.().forEach((track) => { track.enabled = camOn; });
    localStreamRef.current = stream;
    if (selfVideoRef.current) {
      selfVideoRef.current.srcObject = stream;
      selfVideoRef.current.play?.().catch(() => {});
    }
    return stream;
  };

  const fmt = (s) => `${String(Math.floor(s / 60)).padStart(2, '0')}:${String(s % 60).padStart(2, '0')}`;

  const sendChat = () => {
    if (!chatInput.trim()) return;
    setChat([...chat, { from: 'me', text: chatInput, t: fmt(seconds) }]);
    sendPrivate({ type: 'chat', text: chatInput });
    setChatInput('');
  };

  return (
    <div className="th-call">
      <div className="th-call-header">
        <div className="th-call-rec mono">
          <span className="th-call-dot" /> LIVE · {fmt(seconds)}
        </div>
        <div className="th-call-with mono">
          {mode === 'video' ? '▶' : mode === 'phone' ? '☏' : '✎'} {provider.name.toUpperCase()}
        </div>
        <div className="th-call-topic mono">RE: {topicLabel.toUpperCase()}</div>
      </div>

      <div className="th-call-stage">
        {/* Provider video tile (large) */}
        <div className="th-tile th-tile-provider">
          {mode === 'video' ? (
            <div className="th-tile-video">
              <video ref={remoteVideoRef} autoPlay playsInline />
              <div className="th-tile-avatar">{provider.avatar || '◐'}</div>
              <div className="th-tile-name serif">{provider.name}</div>
              <div className="th-tile-role mono">{provider.role}</div>
              <div className="th-tile-pulse" />
            </div>
          ) : mode === 'message' ? (
            <div className="th-tile-phone">
              <div className="th-tile-phone-icon">✎</div>
              <div className="th-tile-name serif">Private support room</div>
              <div className="th-tile-role mono">MESSAGE FIRST · AUDIO OR VIDEO WHEN NEEDED</div>
              <div className="th-tile-waveform">
                {Array.from({ length: 24 }).map((_, i) => (
                  <span key={i} style={{ animationDelay: `${i * 0.07}s` }} />
                ))}
              </div>
            </div>
          ) : (
            <div className="th-tile-phone">
              <div className="th-tile-phone-icon">☏</div>
              <div className="th-tile-name serif">{provider.name}</div>
              <div className="th-tile-role mono">VOICE CALL · {fmt(seconds)}</div>
              <div className="th-tile-waveform">
                {Array.from({ length: 24 }).map((_, i) => (
                  <span key={i} style={{ animationDelay: `${i * 0.07}s` }} />
                ))}
              </div>
            </div>
          )}
        </div>

        {/* Self preview (small) */}
        {mode === 'video' && (
          <div className="th-tile th-tile-self">
            {camOn ? (
              <div className="th-tile-self-video">
                <video ref={selfVideoRef} autoPlay playsInline muted />
                <div className="th-tile-self-label mono">YOU</div>
              </div>
            ) : (
              <div className="th-tile-self-off">
                <div className="th-tile-self-letter">{fname[0]}</div>
                <div className="mono" style={{ fontSize: 10 }}>CAMERA OFF</div>
              </div>
            )}
          </div>
        )}

        {/* Side panel — context + chat */}
        <aside className={`th-call-side ${chatOpen ? 'is-chat' : ''}`}>
          {!chatOpen && (
            <>
              <div className="th-call-side-h mono">— ON THIS CALL</div>
              <div className="th-call-side-card">
                <div className="th-h mono">— TOPIC</div>
                <div className="th-call-side-topic">{topicLabel}</div>
                {notes && <div className="th-call-side-notes">"{notes}"</div>}
              </div>
              <div className="th-call-side-card">
                <div className="th-h mono">— YOUR ACTIVE PLAN</div>
                <ul className="th-call-side-list">
                  <li>BPC-157 · 500mcg subq · daily</li>
                  <li>CJC-1295 / Ipamorelin · 300mcg · pre-bed</li>
                  <li>Started 14 days ago · 2 weeks remaining on vial 1</li>
                </ul>
              </div>
              <div className="th-call-side-card">
                <div className="th-h mono">— LAST CHECK-IN</div>
                <p style={{ fontSize: 13, color: 'var(--ink-mute)', lineHeight: 1.5 }}>
                  "Sleep is noticeably deeper. Recovery between training sessions feels real. Skin a touch drier — wondering if related."
                </p>
                <div className="mono" style={{ fontSize: 10, color: 'var(--ink-mute)', marginTop: 8 }}>4 DAYS AGO</div>
              </div>
            </>
          )}
          {chatOpen && (
            <>
              <div className="th-call-side-h mono">— CHAT</div>
              <div className="th-call-chat">
                {chat.map((m, i) => (
                  <div key={i} className={`th-call-msg th-call-msg-${m.from}`}>
                    <div className="th-call-msg-text">{m.text}</div>
                    <div className="th-call-msg-t mono">{m.t}</div>
                  </div>
                ))}
              </div>
              <div className="th-call-chat-input">
                <input
                  type="text"
                  placeholder="Type a message…"
                  value={chatInput}
                  onChange={(e) => setChatInput(e.target.value)}
                  onKeyDown={(e) => { if (e.key === 'Enter') sendChat(); }}
                />
                <button onClick={sendChat}>Send</button>
              </div>
            </>
          )}
        </aside>
      </div>

      <div className="th-call-controls">
        {mode !== 'message' && (
          <button
            className={`th-ctl ${muted ? 'is-off' : ''}`}
            onClick={() => setMuted(!muted)}
            title={muted ? 'Unmute' : 'Mute'}
          >
            <span className="th-ctl-icon">{muted ? '🔇' : '🎤'}</span>
            <span className="th-ctl-label mono">{muted ? 'MUTED' : 'MIC ON'}</span>
          </button>
        )}
        {mode === 'video' && (
          <button
            className={`th-ctl ${!camOn ? 'is-off' : ''}`}
            onClick={() => setCamOn(!camOn)}
            title={camOn ? 'Turn camera off' : 'Turn camera on'}
          >
            <span className="th-ctl-icon">{camOn ? '📷' : '🚫'}</span>
            <span className="th-ctl-label mono">{camOn ? 'CAMERA ON' : 'CAMERA OFF'}</span>
          </button>
        )}
        <button
          className={`th-ctl ${chatOpen ? 'is-on' : ''}`}
          onClick={() => setChatOpen(!chatOpen)}
        >
          <span className="th-ctl-icon">💬</span>
          <span className="th-ctl-label mono">CHAT</span>
        </button>
        <button className="th-ctl th-ctl-end" onClick={onEnd}>
          <span className="th-ctl-icon">✕</span>
          <span className="th-ctl-label mono">END CALL</span>
        </button>
      </div>
    </div>
  );
};

// ─── 4. SUMMARY ──────────────────────────────────────────────────────────

const SummaryStep = ({ fname, provider, mode, slot, topic, onDone }) => {
  const topicLabel = TOPICS.find(t => t.id === topic)?.label || 'Plan adjustment';
  return (
    <div className="th-stage">
      <div className="th-card th-summary-card">
        <div className="th-eyebrow mono">— 1:1 SUPPORT</div>
        <h1 className="serif th-h1">Your private support thread is on your record.</h1>
        <p className="th-sub">Messages, audio, video, and follow-up notes stay connected to your member profile so the care team has the context next time.</p>

        <div className="th-sum-meta">
          <div><span className="mono th-h">— DATE</span><div>{slot ? `${slot.day}, ${slot.date} · ${slot.time}` : 'Today'}</div></div>
          <div><span className="mono th-h">— PROVIDER</span><div>{provider.name}</div></div>
          <div><span className="mono th-h">— MODE</span><div>{mode === 'video' ? 'Video' : mode === 'phone' ? 'Audio' : 'Message'}</div></div>
          <div><span className="mono th-h">— TOPIC</span><div>{topicLabel}</div></div>
        </div>

        <div className="th-sum-section">
          <div className="th-h mono">— WHAT WE TALKED ABOUT</div>
          <ul className="th-sum-list">
            <li>Sleep noticeably deeper since starting CJC/Ipa stack — confirmed expected response.</li>
            <li>Mild skin dryness raised; likely seasonal/hydration rather than peptide-driven. Watch for two weeks.</li>
            <li>Recovery between training sessions improved.</li>
          </ul>
        </div>

        <div className="th-sum-section">
          <div className="th-h mono">— WHAT'S CHANGING</div>
          <ul className="th-sum-list th-sum-list-actions">
            <li><strong>No dose change this month.</strong> You're responding well — let it run.</li>
            <li><strong>Add 2L water/day target.</strong> Re-check skin in two weeks.</li>
            <li><strong>Use this same private room</strong> when the follow-up should stay out of the provider care room.</li>
          </ul>
        </div>

        <div className="th-sum-section">
          <div className="th-h mono">— ANYTHING URGENT</div>
          <p style={{ fontSize: 14, color: 'var(--ink-mute)', lineHeight: 1.6 }}>
            None flagged. If something changes between now and your next call, message us — we read inbound within one business day.
          </p>
        </div>

        <div className="th-actions">
          <button className="btn btn-soft" onClick={onDone}>Back to dashboard</button>
          <button className="btn btn-primary btn-lg" onClick={onDone}>
            Got it — see you in four weeks →
          </button>
        </div>
      </div>
    </div>
  );
};

window.Telehealth = Telehealth;
