// foro.jsx — Foro general de Serenia (v20, rediseño)
// Cargado en SerenIA.html: <script type="text/babel" src="./foro.jsx">
//
// Este archivo se ejecuta ANTES que el script principal, así que window.Turtle,
// window.SupaForo, window.VoiceButton, window.haptic y _db solo existen en tiempo
// de render: siempre se leen dentro de los componentes, nunca al cargar.
// API conservada: SupaForo.getAll / add / react / delete / getReplies / addReply / deleteReply.

// ── Utilidades ────────────────────────────────────────────────────────────────
// post_type ya viene de Supabase (columna existe); localStorage como respaldo de caché
function foroGetTypeCache() {
  try { return JSON.parse(localStorage.getItem('serenia_foro_types') || '{}'); } catch (_) { return {}; }
}
function foroCacheType(postId, type) {
  try {
    const t = foroGetTypeCache(); t[postId] = type;
    localStorage.setItem('serenia_foro_types', JSON.stringify(t));
  } catch (_) {}
}
function foroDb() {
  try { if (typeof _db !== 'undefined' && _db) return _db; } catch (_) {}
  return window._db || null;
}

const FORO_REACTIONS = ['💚', '🤗', '🌿', '💪', '✨'];
const FORO_REACTION_LABELS = { '💚': 'Te acompaño', '🤗': 'Un abrazo', '🌿': 'Calma', '💪': 'Fuerza', '✨': 'Inspira' };
const FORO_MAX_POST = 1000;
const FORO_MAX_REPLY = 500;
const FORO_MAX_W = 640; // ancho máximo del contenido en pantallas anchas (iPad)
const FORO_TYPES = {
  pregunta: { label: 'Pregunta', plural: 'Preguntas', desc: 'Pide consejo o experiencias', pose: 'chat' },
  desahogo: { label: 'Desahogo', plural: 'Desahogos', desc: 'Cuenta cómo estás, sin filtro', pose: 'sit' },
};
function foroType(post) { return post && post.post_type === 'pregunta' ? 'pregunta' : 'desahogo'; }

function foroTimeAgo(ts) {
  const t = new Date(ts).getTime();
  if (isNaN(t)) return '';
  const s = Math.max(0, (Date.now() - t) / 1000);
  if (s < 45) return 'ahora';
  const m = Math.round(s / 60);
  if (m < 60) return `hace ${m} min`;
  const h = Math.round(m / 60);
  if (h < 24) return `hace ${h} h`;
  const d = Math.round(h / 24);
  if (d === 1) return 'ayer';
  if (d < 7) return `hace ${d} días`;
  const dt = new Date(t);
  const sameYear = dt.getFullYear() === new Date().getFullYear();
  return dt.toLocaleDateString('es-MX', sameYear ? { day: 'numeric', month: 'short' } : { day: 'numeric', month: 'short', year: 'numeric' });
}
function foroFullDate(ts) {
  const d = new Date(ts);
  if (isNaN(d.getTime())) return '';
  return d.toLocaleString('es-MX', { day: 'numeric', month: 'long', hour: '2-digit', minute: '2-digit' });
}
function foroTotalReactions(post) {
  return Object.values(post.reactions || {}).reduce((s, l) => s + (Array.isArray(l) ? l.length : 0), 0);
}
function foroReducedMotion() {
  try { return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; } catch (_) { return false; }
}
// Conteo de respuestas por publicación (una consulta ligera; el foro es pequeño)
async function foroFetchReplyCounts(ids) {
  const db = foroDb();
  const counts = {};
  if (!db || !ids.length) return counts;
  for (let i = 0; i < ids.length; i += 60) {
    const chunk = ids.slice(i, i + 60);
    const { data, error } = await db.from('foro_replies').select('post_id').in('post_id', chunk);
    if (error) throw error;
    (data || []).forEach(r => { counts[r.post_id] = (counts[r.post_id] || 0) + 1; });
  }
  return counts;
}
// Una publicación con su autor (para las que llegan por realtime sin el join)
async function foroFetchPost(id) {
  const db = foroDb();
  if (!db) return null;
  const { data, error } = await db
    .from('foro_posts')
    .select('*, author:profiles!foro_posts_user_id_fkey(name, user_type)')
    .eq('id', id).single();
  if (error) throw error;
  return data;
}
// Color de avatar estable por nombre (tintes claros; el texto siempre va oscuro)
const FORO_AVATAR_TINTS = ['#D4E3D7', '#EBDDC0', '#D4E2E8', '#E1EAD3', '#EFD9A8', '#E3D9EA', '#F0D9CF'];
function foroAvatarTint(name) {
  let h = 0; for (let i = 0; i < (name || '').length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0;
  return FORO_AVATAR_TINTS[h % FORO_AVATAR_TINTS.length];
}

// ── Estilos del foro (una sola vez) ──────────────────────────────────────────
if (typeof document !== 'undefined' && !document.getElementById('foro-style')) {
  const s = document.createElement('style');
  s.id = 'foro-style';
  s.textContent = `
    @keyframes foroFadeUp { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
    @keyframes foroFadeIn { from { opacity: 0; } to { opacity: 1; } }
    @keyframes foroSheetUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
    @keyframes foroPop { 0% { transform: scale(1); } 40% { transform: scale(1.45) rotate(-8deg); } 100% { transform: scale(1); } }
    @keyframes foroShimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
    .foro-chips { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; }
    .foro-chips::-webkit-scrollbar { display: none; }
    .foro-card { transition: transform .15s ease, box-shadow .15s ease; }
    .foro-card:active { transform: scale(.992); }
    .foro-tap { transition: transform .12s ease, opacity .12s ease, background-color .15s ease; }
    .foro-tap:active { transform: scale(.96); }
    .foro-react { transition: background-color .15s ease, border-color .15s ease, color .15s ease, transform .12s ease; }
    .foro-react:active { transform: scale(.94); }
    .foro-pop { animation: foroPop .45s cubic-bezier(.34,1.56,.64,1); }
    .foro-ta, .foro-input { -webkit-appearance: none; appearance: none; }
    .foro-ta::placeholder, .foro-input::placeholder { color: var(--foro-ph, #8B978D); opacity: 1; }
    .foro-ta:focus, .foro-input:focus { outline: none; }
    .foro-clamp { display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
    @media (prefers-reduced-motion: reduce) {
      .foro-motion, .foro-motion *, .foro-card, .foro-tap, .foro-react, .foro-pop { animation: none !important; transition: none !important; }
    }
  `;
  document.head.appendChild(s);
}

// ── Piezas pequeñas ───────────────────────────────────────────────────────────
function ForoAvatar({ p, name, anon, type = 'desahogo', size = 38 }) {
  const T = window.Turtle;
  const initial = (name || 'S').trim().charAt(0).toUpperCase();
  if (anon) {
    return (
      <div aria-hidden="true" style={{
        width: size, height: size, borderRadius: '50%', flexShrink: 0,
        background: p.primarySoft, display: 'grid', placeItems: 'center', overflow: 'hidden',
      }}>
        {T ? <T pose={FORO_TYPES[type].pose} size={Math.round(size * 0.72)} /> : <span style={{ fontSize: size * 0.45 }}>🐢</span>}
      </div>
    );
  }
  return (
    <div aria-hidden="true" style={{
      width: size, height: size, borderRadius: '50%', flexShrink: 0,
      background: foroAvatarTint(name), color: '#2C3A2E',
      display: 'grid', placeItems: 'center', fontSize: Math.round(size * 0.4), fontWeight: 600,
      fontFamily: 'Fraunces, serif',
    }}>{initial}</div>
  );
}

function ForoTypeTag({ p, type, small }) {
  const isQ = type === 'pregunta';
  const color = isQ ? p.shellBlue : p.primaryDeep;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5, flexShrink: 0,
      padding: small ? '2px 8px' : '3px 10px', borderRadius: 999,
      background: color + '1F', color, fontSize: small ? 11 : 11.5, fontWeight: 600, letterSpacing: '0.01em',
    }}>
      <span style={{ width: 6, height: 6, borderRadius: '50%', background: color }} />
      {FORO_TYPES[type].label}
    </span>
  );
}

// Menú "⋯" con acciones propias (se cierra al tocar fuera)
function ForoMenu({ p, items, label = 'Más opciones', size = 30 }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) return;
    const close = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('pointerdown', close, true);
    return () => document.removeEventListener('pointerdown', close, true);
  }, [open]);
  return (
    <div ref={ref} onClick={e => e.stopPropagation()} style={{ position: 'relative', flexShrink: 0 }}>
      <button type="button" aria-label={label} aria-haspopup="menu" aria-expanded={open}
        className="foro-tap" onClick={() => setOpen(o => !o)} style={{
          width: size, height: size, borderRadius: '50%', border: 'none', cursor: 'pointer',
          background: open ? p.primarySoft : 'transparent', color: p.inkSoft,
          display: 'grid', placeItems: 'center', padding: 0,
        }}>
        <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
          <circle cx="5" cy="12" r="2" /><circle cx="12" cy="12" r="2" /><circle cx="19" cy="12" r="2" />
        </svg>
      </button>
      {open && (
        <div role="menu" className="foro-motion" style={{
          position: 'absolute', right: 0, top: 'calc(100% + 4px)', zIndex: 40, minWidth: 168,
          background: p.surface, border: `1px solid ${p.line}`, borderRadius: 14, padding: 6,
          boxShadow: '0 10px 30px rgba(20,30,25,0.16)', animation: 'foroFadeUp .16s ease',
        }}>
          {items.map(it => (
            <button key={it.label} type="button" role="menuitem"
              onClick={() => { setOpen(false); it.onClick?.(); }} style={{
                width: '100%', textAlign: 'left', padding: '9px 12px', borderRadius: 10, border: 'none',
                background: 'transparent', cursor: 'pointer', fontSize: 13.5, fontWeight: 500,
                color: it.danger ? '#C5524B' : p.ink, display: 'flex', alignItems: 'center', gap: 8,
                fontFamily: 'inherit',
              }}>{it.label}</button>
          ))}
        </div>
      )}
    </div>
  );
}

// Barra de reacciones agrupadas: solo las que tienen conteo + botón para abrir las cinco
function ForoReactionBar({ p, post, userId, onReact }) {
  const [open, setOpen] = React.useState(false);
  const [pop, setPop] = React.useState(null);
  const popT = React.useRef(null);
  React.useEffect(() => () => clearTimeout(popT.current), []);
  const reactions = post.reactions || {};
  const withCount = FORO_REACTIONS.filter(e => (reactions[e] || []).length > 0);
  const shown = open ? FORO_REACTIONS : withCount;
  const react = (e, emoji) => {
    e.stopPropagation();
    if (!userId) return;
    const mine = (reactions[emoji] || []).includes(userId);
    if (!mine) { setPop(emoji); clearTimeout(popT.current); popT.current = setTimeout(() => setPop(null), 500); }
    window.haptic?.('light');
    onReact(emoji);
    if (open) setOpen(false);
  };
  return (
    <div onClick={e => e.stopPropagation()} style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 6, minWidth: 0 }}>
      {shown.map(emoji => {
        const list = reactions[emoji] || [];
        const mine = !!userId && list.includes(userId);
        return (
          <button key={emoji} type="button" className="foro-react" aria-pressed={mine}
            title={FORO_REACTION_LABELS[emoji]}
            aria-label={`${FORO_REACTION_LABELS[emoji]}${list.length ? ', ' + list.length : ''}`}
            onClick={e => react(e, emoji)} style={{
              height: 30, padding: '0 10px', borderRadius: 999, cursor: userId ? 'pointer' : 'default',
              border: `1px solid ${mine ? p.primaryDeep : p.line}`,
              background: mine ? p.primarySoft : p.bg, color: mine ? p.primaryDeep : p.inkSoft,
              fontSize: 13, fontWeight: 600, display: 'inline-flex', alignItems: 'center', gap: 5,
              animation: open ? 'foroFadeUp .18s ease both' : 'none', fontFamily: 'inherit',
            }}>
            <span className={pop === emoji ? 'foro-pop' : ''} style={{ fontSize: 15, lineHeight: 1, display: 'inline-block' }}>{emoji}</span>
            {list.length > 0 && <span>{list.length}</span>}
          </button>
        );
      })}
      {userId && (
        <button type="button" className="foro-react" aria-expanded={open}
          aria-label={open ? 'Cerrar reacciones' : 'Reaccionar'}
          onClick={e => { e.stopPropagation(); setOpen(o => !o); }} style={{
            height: 30, padding: open ? '0 10px' : (withCount.length ? '0 9px' : '0 12px'), borderRadius: 999, cursor: 'pointer',
            border: `1px dashed ${open ? p.primaryDeep : p.line}`, background: 'transparent',
            color: open ? p.primaryDeep : p.inkMute, fontSize: 12.5, fontWeight: 600,
            display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: 'inherit',
          }}>
          {open ? '×' : (
            <>
              <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                <circle cx="12" cy="12" r="9" /><path d="M8.5 14.5c.9 1.2 2.1 1.8 3.5 1.8s2.6-.6 3.5-1.8" /><path d="M9 9.5h.01M15 9.5h.01" />
              </svg>
              {withCount.length === 0 && 'Reaccionar'}
            </>
          )}
        </button>
      )}
    </div>
  );
}

// Esqueleto de carga (en lugar de "Cargando…")
function ForoSkeleton({ p, n = 3 }) {
  const bar = (w, h = 12, extra = {}) => (
    <div style={{
      width: w, height: h, borderRadius: 999,
      background: `linear-gradient(90deg, ${p.line}88 25%, ${p.line} 50%, ${p.line}88 75%)`,
      backgroundSize: '200% 100%', animation: 'foroShimmer 1.4s linear infinite', ...extra,
    }} />
  );
  return (
    <div className="foro-motion" aria-busy="true" aria-label="Cargando publicaciones" style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
      {Array.from({ length: n }).map((_, i) => (
        <div key={i} style={{ background: p.surface, border: `1px solid ${p.line}`, borderRadius: 20, padding: 16 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
            {bar(38, 38, { borderRadius: '50%', flexShrink: 0 })}
            <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 7 }}>{bar('42%')}{bar('26%', 9)}</div>
            {bar(72, 22)}
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 14 }}>{bar('100%')}{bar('92%')}{bar('64%')}</div>
          <div style={{ display: 'flex', gap: 6 }}>{bar(58, 30)}{bar(58, 30)}{bar(96, 30, { marginLeft: 'auto' })}</div>
        </div>
      ))}
    </div>
  );
}

// Estado vacío con la tortuga
function ForoEmpty({ p, pose = 'rest', title, text, action, onAction, secondary, onSecondary }) {
  const T = window.Turtle;
  return (
    <div className="foro-motion" style={{ textAlign: 'center', padding: '44px 20px 30px', animation: 'foroFadeIn .3s ease' }}>
      <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 12 }}>
        {T ? <T pose={pose} size={92} animate /> : <div style={{ fontSize: 44 }}>🐢</div>}
      </div>
      <div style={{ fontFamily: 'Fraunces, serif', fontSize: 19, fontWeight: 500, color: p.ink, lineHeight: 1.25 }}>{title}</div>
      {text && <div style={{ fontSize: 13.5, color: p.inkSoft, lineHeight: 1.55, marginTop: 8, maxWidth: 300, margin: '8px auto 0', textWrap: 'pretty' }}>{text}</div>}
      {(action || secondary) && (
        <div style={{ display: 'flex', gap: 8, justifyContent: 'center', marginTop: 18, flexWrap: 'wrap' }}>
          {action && (
            <button type="button" className="foro-tap" onClick={onAction} style={{
              padding: '10px 20px', borderRadius: 999, border: 'none', cursor: 'pointer',
              background: p.primaryDeep, color: '#fff', fontSize: 13.5, fontWeight: 600, fontFamily: 'inherit',
            }}>{action}</button>
          )}
          {secondary && (
            <button type="button" className="foro-tap" onClick={onSecondary} style={{
              padding: '10px 18px', borderRadius: 999, cursor: 'pointer', background: 'transparent',
              border: `1.5px solid ${p.line}`, color: p.primaryDeep, fontSize: 13.5, fontWeight: 600, fontFamily: 'inherit',
            }}>{secondary}</button>
          )}
        </div>
      )}
    </div>
  );
}

// Guía de convivencia (desplegable; recuerda si ya se cerró)
function ForoGuide({ p }) {
  const [open, setOpen] = React.useState(() => {
    try { return localStorage.getItem('serenia_foro_guia') !== 'ok'; } catch (_) { return true; }
  });
  const dismiss = () => { setOpen(false); try { localStorage.setItem('serenia_foro_guia', 'ok'); } catch (_) {} };
  const openSOS = () => { try { window.dispatchEvent(new Event('serenia-open-sos')); } catch (_) {} };
  const rules = [
    ['Escuchamos antes de opinar.', 'Lee completo y responde con cuidado.'],
    ['No diagnosticamos ni recetamos.', 'Compartimos experiencias, no tratamientos.'],
    ['Si alguien está en riesgo, no lo dejes pasar.', 'Avísale con el botón SOS de Serenia.'],
  ];
  return (
    <div style={{
      borderRadius: 18, border: `1px solid ${open ? p.accent + '66' : p.line}`,
      background: open ? p.accentSoft + '55' : p.surface, overflow: 'hidden',
      transition: 'background-color .2s ease, border-color .2s ease',
    }}>
      <button type="button" onClick={() => open ? dismiss() : setOpen(true)} aria-expanded={open} style={{
        width: '100%', display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px',
        background: 'transparent', border: 'none', cursor: 'pointer', textAlign: 'left', color: p.ink, fontFamily: 'inherit',
      }}>
        <span aria-hidden="true" style={{ fontSize: 17 }}>🌿</span>
        <span style={{ flex: 1, fontSize: 13.5, fontWeight: 600 }}>Cómo convivimos aquí</span>
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={p.inkMute} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"
          aria-hidden="true" style={{ transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .2s ease' }}>
          <path d="M6 9l6 6 6-6" />
        </svg>
      </button>
      {open && (
        <div className="foro-motion" style={{ padding: '0 14px 14px', animation: 'foroFadeIn .2s ease' }}>
          <ol style={{ margin: 0, padding: 0, listStyle: 'none', display: 'flex', flexDirection: 'column', gap: 8 }}>
            {rules.map(([a, b], i) => (
              <li key={i} style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
                <span aria-hidden="true" style={{
                  width: 20, height: 20, borderRadius: '50%', flexShrink: 0, marginTop: 1,
                  background: p.accent, color: '#2C3A2E', fontSize: 11, fontWeight: 700, display: 'grid', placeItems: 'center',
                }}>{i + 1}</span>
                <span style={{ fontSize: 13, lineHeight: 1.5, color: p.ink }}>
                  <b style={{ fontWeight: 600 }}>{a}</b> <span style={{ color: p.inkSoft }}>{b}</span>
                </span>
              </li>
            ))}
          </ol>
          <div style={{ display: 'flex', gap: 8, marginTop: 12, flexWrap: 'wrap' }}>
            <button type="button" className="foro-tap" onClick={openSOS} style={{
              padding: '8px 14px', borderRadius: 999, border: 'none', cursor: 'pointer',
              background: '#C5524B', color: '#fff', fontSize: 12.5, fontWeight: 600, fontFamily: 'inherit',
            }}>Abrir SOS</button>
            <button type="button" className="foro-tap" onClick={dismiss} style={{
              padding: '8px 14px', borderRadius: 999, cursor: 'pointer', background: 'transparent',
              border: `1.5px solid ${p.accent}88`, color: p.ink, fontSize: 12.5, fontWeight: 600, fontFamily: 'inherit',
            }}>Entendido</button>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Tarjeta de publicación (feed y detalle) ───────────────────────────────────
function ForoCard({ p, post, userId, replyCount = 0, onOpen, onReact, onDelete, expanded = false, index = 0 }) {
  const isAnon = !!post.anonymous;
  const name = isAnon ? 'Anónimo' : (post.author?.name || post.author_name || 'Alguien de Serenia');
  const type = foroType(post);
  const isOwn = !!userId && post.user_id === userId;
  const [more, setMore] = React.useState(false);
  const [clamped, setClamped] = React.useState(false);
  const textRef = React.useRef(null);

  React.useLayoutEffect(() => {
    if (expanded || more) return;
    const el = textRef.current;
    if (el) setClamped(el.scrollHeight > el.clientHeight + 1);
  }, [post.text, expanded, more]);

  const clickable = !expanded && !!onOpen;
  const open = (e) => { if (clickable) onOpen(); };
  const onKey = (e) => { if (clickable && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); onOpen(); } };
  const menuItems = isOwn && onDelete ? [{ label: 'Eliminar publicación', danger: true, onClick: () => { if (window.confirm('¿Eliminar esta publicación? No se puede deshacer.')) onDelete(); } }] : null;

  return (
    <article className={clickable ? 'foro-card foro-motion' : 'foro-motion'} onClick={open} onKeyDown={onKey}
      role={clickable ? 'button' : undefined} tabIndex={clickable ? 0 : undefined}
      aria-label={clickable ? `Abrir ${FORO_TYPES[type].label.toLowerCase()} de ${name}` : undefined}
      style={{
        background: p.surface, border: `1px solid ${p.line}`, borderRadius: 20,
        padding: expanded ? '16px 16px 14px' : '14px 16px 12px',
        boxShadow: p.dark ? 'none' : '0 1px 2px rgba(44,58,46,0.05)',
        cursor: clickable ? 'pointer' : 'default',
        animation: index < 12 ? `foroFadeUp .28s ease ${Math.min(index, 8) * 0.03}s both` : 'none',
        outlineColor: p.primaryDeep,
      }}>
      {/* Cabecera: avatar · nombre/hora · etiqueta · menú */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <ForoAvatar p={p} name={name} anon={isAnon} type={type} size={38} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>
            <span style={{ fontSize: 13.5, fontWeight: 600, color: p.ink, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{name}</span>
            {isOwn && <span style={{ fontSize: 10.5, fontWeight: 700, color: p.dark ? p.accent : '#8A6B2F', background: p.accentSoft, padding: '1px 6px', borderRadius: 999, flexShrink: 0 }}>Tú</span>}
          </div>
          <time dateTime={post.created_at} title={foroFullDate(post.created_at)} style={{ fontSize: 11.5, color: p.inkMute, display: 'block', marginTop: 1 }}>
            {foroTimeAgo(post.created_at)}
          </time>
        </div>
        <ForoTypeTag p={p} type={type} />
        {menuItems && <ForoMenu p={p} items={menuItems} label="Opciones de tu publicación" />}
      </div>

      {/* Texto */}
      <div style={{ marginTop: 10 }}>
        <div ref={textRef} className={!expanded && !more ? 'foro-clamp' : undefined} style={{
          fontSize: expanded ? 16 : 14.5, lineHeight: expanded ? 1.65 : 1.55, color: p.ink,
          whiteSpace: 'pre-wrap', overflowWrap: 'anywhere',
          fontFamily: expanded ? 'Fraunces, serif' : 'inherit',
          WebkitLineClamp: !expanded && !more ? 5 : undefined,
        }}>{post.text}</div>
        {!expanded && clamped && !more && (
          <button type="button" onClick={e => { e.stopPropagation(); setMore(true); }} style={{
            marginTop: 4, padding: '4px 0', border: 'none', background: 'transparent', cursor: 'pointer',
            color: p.primaryDeep, fontSize: 13, fontWeight: 600, fontFamily: 'inherit',
          }}>Leer más</button>
        )}
      </div>

      {/* Pie: reacciones · respuestas */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, flexWrap: 'wrap' }}>
        <ForoReactionBar p={p} post={post} userId={userId} onReact={onReact} />
        {!expanded && (
          <button type="button" className="foro-tap" onClick={e => { e.stopPropagation(); onOpen?.(); }}
            aria-label={replyCount > 0 ? `${replyCount} respuesta${replyCount !== 1 ? 's' : ''}` : 'Responder'} style={{
              marginLeft: 'auto', height: 30, padding: '0 12px', borderRadius: 999, cursor: 'pointer',
              border: replyCount > 0 ? 'none' : `1px solid ${p.line}`,
              background: replyCount > 0 ? p.primaryDeep : 'transparent',
              color: replyCount > 0 ? '#fff' : p.inkSoft, fontSize: 12.5, fontWeight: 600,
              display: 'inline-flex', alignItems: 'center', gap: 6, fontFamily: 'inherit', flexShrink: 0,
            }}>
            <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <path d="M21 12a8 8 0 0 1-11.6 7.1L4 20l1.1-4.4A8 8 0 1 1 21 12z" />
            </svg>
            {replyCount > 0 ? `${replyCount} respuesta${replyCount !== 1 ? 's' : ''}` : 'Responder'}
          </button>
        )}
      </div>
    </article>
  );
}

// ── Pantalla principal del Foro ───────────────────────────────────────────────
function ForoGeneralScreen({ p, onBack, userId, user }) {
  const [posts, setPosts] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [loadError, setLoadError] = React.useState(false);
  const [openPost, setOpenPost] = React.useState(null);
  const [showComposer, setShowComposer] = React.useState(false);
  const [composerType, setComposerType] = React.useState('desahogo');
  const [filter, setFilter] = React.useState('all'); // 'all' | 'pregunta' | 'desahogo' | 'mine'
  const [search, setSearch] = React.useState('');
  const [toast, setToast] = React.useState('');
  const postsRef = React.useRef(posts); postsRef.current = posts;
  const feedRef = React.useRef(null);
  const feedScroll = React.useRef(0);
  const toastT = React.useRef(null);

  const notify = (msg) => {
    setToast(msg); clearTimeout(toastT.current);
    toastT.current = setTimeout(() => setToast(''), 2600);
  };
  React.useEffect(() => () => clearTimeout(toastT.current), []);

  const withType = (row, cache) => ({ ...row, post_type: row.post_type || (cache || foroGetTypeCache())[row.id] || 'desahogo' });

  const loadPosts = () => {
    if (!window.SupaForo) { setLoading(false); return; }
    setLoadError(false);
    window.SupaForo.getAll()
      .then(data => {
        const cache = foroGetTypeCache();
        const list = (data || []).map(p2 => withType(p2, cache));
        setPosts(list);
        setLoading(false);
        // Conteo de respuestas en segundo plano (no bloquea el feed)
        foroFetchReplyCounts(list.map(p2 => p2.id))
          .then(counts => setPosts(ps => ps.map(p2 => counts[p2.id] != null ? { ...p2, reply_count: counts[p2.id] } : p2)))
          .catch(() => {});
      })
      .catch(err => { console.error('Foro getAll error:', err); setLoadError(true); setLoading(false); });
  };
  React.useEffect(() => { loadPosts(); }, []);

  // Tiempo real: publicaciones nuevas, reacciones de otros, borrados y conteo de respuestas
  React.useEffect(() => {
    const db = foroDb();
    if (!db) return;
    const cache = foroGetTypeCache();
    const channel = db.channel('foro-rt')
      .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'foro_posts' }, ({ new: row }) => {
        if (!row?.id) return;
        if (postsRef.current.some(p2 => p2.id === row.id)) return; // ya lo publicó este usuario
        setPosts(prev => prev.some(p2 => p2.id === row.id) ? prev : [withType(row, cache), ...prev]);
        // Traer el autor (el evento no incluye el join)
        foroFetchPost(row.id).then(full => {
          if (full) setPosts(prev => prev.map(p2 => p2.id === full.id ? { ...p2, ...withType(full, cache) } : p2));
        }).catch(() => {});
      })
      .on('postgres_changes', { event: 'UPDATE', schema: 'public', table: 'foro_posts' }, ({ new: row }) => {
        if (!row?.id) return;
        setPosts(prev => prev.map(p2 => p2.id === row.id
          ? { ...p2, text: row.text ?? p2.text, reactions: row.reactions || p2.reactions, post_type: row.post_type || p2.post_type }
          : p2));
      })
      .on('postgres_changes', { event: 'DELETE', schema: 'public', table: 'foro_posts' }, ({ old }) => {
        if (!old?.id) return;
        setPosts(prev => prev.filter(p2 => p2.id !== old.id));
        setOpenPost(op => (op && op.id === old.id) ? null : op);
      })
      .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'foro_replies' }, ({ new: row }) => {
        if (!row?.post_id || row.user_id === userId) return; // las propias se cuentan al enviarlas
        setPosts(prev => prev.map(p2 => p2.id === row.post_id ? { ...p2, reply_count: (p2.reply_count || 0) + 1 } : p2));
      })
      .on('postgres_changes', { event: 'DELETE', schema: 'public', table: 'foro_replies' }, ({ old }) => {
        if (!old?.post_id) return;
        setPosts(prev => prev.map(p2 => p2.id === old.post_id ? { ...p2, reply_count: Math.max(0, (p2.reply_count || 0) - 1) } : p2));
      })
      .subscribe();
    return () => { try { db.removeChannel(channel); } catch (_) {} };
  }, [userId]);

  // Conservar la posición del feed al volver del detalle
  React.useLayoutEffect(() => {
    if (!openPost && feedRef.current) feedRef.current.scrollTop = feedScroll.current;
  }, [openPost]);

  const handleDelete = async (postId) => {
    if (!userId || !window.SupaForo) return;
    try {
      await window.SupaForo.delete(postId, userId);
      setPosts(ps => ps.filter(p2 => p2.id !== postId));
      setOpenPost(op => (op && op.id === postId) ? null : op);
      notify('Publicación eliminada');
    } catch (err) {
      console.error('Foro delete error:', err);
      alert('No se pudo eliminar la publicación. Verifica tu conexión e intenta de nuevo.');
    }
  };

  const handlePublish = async (draft) => {
    if (!userId || !window.SupaForo) throw new Error('Sin sesión');
    const newPost = await window.SupaForo.add(userId, draft); // los errores los muestra el compositor
    foroCacheType(newPost.id, newPost.post_type || draft.post_type || 'desahogo');
    setPosts(ps => [{ ...withType(newPost), reply_count: 0 }, ...ps.filter(p2 => p2.id !== newPost.id)]);
    setShowComposer(false);
    setSearch('');
    if (filter !== 'all' && filter !== 'mine' && filter !== (newPost.post_type || draft.post_type)) setFilter('all');
    feedScroll.current = 0;
    requestAnimationFrame(() => { if (feedRef.current) feedRef.current.scrollTo({ top: 0, behavior: foroReducedMotion() ? 'auto' : 'smooth' }); });
    window.haptic?.('light');
    notify('Tu publicación ya está en el foro 🌿');
  };

  // Reacción optimista: se ve al instante y se concilia con la respuesta del servidor
  const handleReact = async (postId, emoji) => {
    if (!userId || !window.SupaForo) return;
    const before = postsRef.current.find(p2 => p2.id === postId);
    if (!before) return;
    const prev = before.reactions || {};
    const list = prev[emoji] || [];
    const optimistic = { ...prev, [emoji]: list.includes(userId) ? list.filter(x => x !== userId) : [...list, userId] };
    setPosts(ps => ps.map(p2 => p2.id === postId ? { ...p2, reactions: optimistic } : p2));
    try {
      const fresh = await window.SupaForo.react(postId, userId, emoji);
      setPosts(ps => ps.map(p2 => p2.id === postId ? { ...p2, reactions: fresh } : p2));
    } catch (err) {
      console.error('Foro react error:', err);
      setPosts(ps => ps.map(p2 => p2.id === postId ? { ...p2, reactions: prev } : p2));
    }
  };

  const handleReplyCountLoaded = (postId, count) => {
    setPosts(ps => ps.map(p2 => p2.id === postId ? { ...p2, reply_count: count } : p2));
  };
  const handleReplied = (postId, delta = 1) => {
    setPosts(ps => ps.map(p2 => p2.id === postId ? { ...p2, reply_count: Math.max(0, (p2.reply_count || 0) + delta) } : p2));
  };

  const openComposer = (type) => { setComposerType(type || (filter === 'pregunta' ? 'pregunta' : 'desahogo')); setShowComposer(true); };

  const counts = {
    all: posts.length,
    pregunta: posts.filter(p2 => foroType(p2) === 'pregunta').length,
    desahogo: posts.filter(p2 => foroType(p2) === 'desahogo').length,
    mine: userId ? posts.filter(p2 => p2.user_id === userId).length : 0,
  };
  const q = search.trim().toLowerCase();
  const visible = posts
    .filter(p2 => filter === 'all' || (filter === 'mine' ? p2.user_id === userId : foroType(p2) === filter))
    .filter(p2 => !q || (p2.text || '').toLowerCase().includes(q) || (!p2.anonymous && (p2.author?.name || '').toLowerCase().includes(q)));

  const chips = [
    { id: 'all', label: 'Todo' },
    { id: 'pregunta', label: 'Preguntas' },
    { id: 'desahogo', label: 'Desahogos' },
    ...(userId ? [{ id: 'mine', label: 'Míos' }] : []),
  ];
  const firstName = (user?.name || '').trim().split(/\s+/)[0] || '';
  const inner = { maxWidth: FORO_MAX_W, margin: '0 auto', width: '100%' };

  if (openPost) {
    const fresh = posts.find(p2 => p2.id === openPost.id) || openPost;
    return (
      <ForoPostDetail p={p} post={fresh} userId={userId} user={user}
        onBack={() => setOpenPost(null)}
        onReact={(emoji) => handleReact(fresh.id, emoji)}
        onDelete={() => handleDelete(fresh.id)}
        onReplyCountLoaded={handleReplyCountLoaded}
        onReplied={handleReplied} />
    );
  }

  let body;
  if (loading) {
    body = <ForoSkeleton p={p} />;
  } else if (loadError && posts.length === 0) {
    body = <ForoEmpty p={p} pose="sad" title="No pudimos cargar el foro"
      text="Revisa tu conexión e inténtalo de nuevo." action="Reintentar" onAction={() => { setLoading(true); loadPosts(); }} />;
  } else if (visible.length === 0) {
    if (q) {
      body = <ForoEmpty p={p} pose="sit" title={<>No encontramos nada con «{search.trim()}»</>}
        text="Prueba con otras palabras o revisa la ortografía." action="Limpiar búsqueda" onAction={() => setSearch('')} />;
    } else if (posts.length === 0) {
      body = <ForoEmpty p={p} pose="rest" title="El foro está esperando tu voz"
        text="Comparte una pregunta, un logro o simplemente cómo estás. Alguien te va a leer."
        action="Escribir la primera publicación" onAction={() => openComposer('desahogo')} />;
    } else if (filter === 'mine') {
      body = <ForoEmpty p={p} pose="chat" title="Todavía no has publicado"
        text="Lo que compartas en el foro aparecerá en esta lista." action="Escribir algo" onAction={() => openComposer('desahogo')} secondary="Ver todo" onSecondary={() => setFilter('all')} />;
    } else {
      const isQ = filter === 'pregunta';
      body = <ForoEmpty p={p} pose="walk" title={isQ ? 'Aún no hay preguntas' : 'Aún no hay desahogos'}
        text={isQ ? 'Sé quien abra la conversación: alguien más se estará preguntando lo mismo.' : 'Este es un lugar para soltar lo que traes. Cuando quieras, aquí está.'}
        action={isQ ? 'Hacer una pregunta' : 'Escribir un desahogo'} onAction={() => openComposer(filter)} secondary="Ver todo" onSecondary={() => setFilter('all')} />;
    }
  } else {
    body = (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        {visible.map((post, i) => (
          <ForoCard key={post.id} p={p} post={post} userId={userId} index={i}
            replyCount={post.reply_count || 0}
            onOpen={() => { feedScroll.current = feedRef.current ? feedRef.current.scrollTop : 0; setOpenPost(post); }}
            onReact={(emoji) => handleReact(post.id, emoji)}
            onDelete={() => handleDelete(post.id)} />
        ))}
        <div style={{ textAlign: 'center', fontSize: 12, color: p.inkMute, padding: '12px 0 4px' }}>
          {visible.length === 1 ? 'Una publicación' : `${visible.length} publicaciones`}{q || filter !== 'all' ? ' en esta vista' : ''} · Gracias por leer con cuidado
        </div>
      </div>
    );
  }

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: p.bg, position: 'relative', color: p.ink }}>

      {/* Cabecera */}
      <div style={{ padding: '10px 16px 0', borderBottom: `1px solid ${p.line}`, flexShrink: 0, background: p.bg }}>
        <div style={inner}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', minHeight: 32 }}>
            <button type="button" onClick={onBack} className="foro-tap" style={{
              background: 'transparent', border: 'none', cursor: 'pointer', color: p.primaryDeep,
              fontSize: 15, fontWeight: 600, padding: '4px 0', fontFamily: 'inherit', display: 'inline-flex', alignItems: 'center', gap: 2,
            }}>‹ Volver</button>
          </div>
          <div style={{ marginTop: 4 }}>
            <h1 style={{ margin: 0, fontFamily: 'Fraunces, serif', fontWeight: 500, fontSize: 24, lineHeight: 1.15, letterSpacing: '-0.01em', color: p.ink }}>
              Foro Serenia
            </h1>
            <div style={{ fontSize: 13, color: p.inkSoft, marginTop: 3 }}>Un lugar para leer y ser leído</div>
          </div>

          {/* Búsqueda (altura fija: no salta al escribir) */}
          <div style={{ position: 'relative', marginTop: 12, height: 40 }}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={p.inkMute} strokeWidth="2.2" strokeLinecap="round" aria-hidden="true"
              style={{ position: 'absolute', left: 13, top: '50%', transform: 'translateY(-50%)', pointerEvents: 'none' }}>
              <circle cx="11" cy="11" r="7" /><path d="M20 20l-3.5-3.5" />
            </svg>
            <input className="foro-input" type="text" inputMode="search" enterKeyHint="search" aria-label="Buscar en el foro"
              value={search} onChange={e => setSearch(e.target.value)} placeholder="Buscar en el foro…"
              style={{
                '--foro-ph': p.inkMute, width: '100%', height: 40, padding: '0 38px 0 38px', boxSizing: 'border-box',
                background: p.surface, border: `1px solid ${p.line}`, borderRadius: 14, color: p.ink,
                fontSize: 14, fontFamily: 'inherit',
              }} />
            {search.length > 0 && (
              <button type="button" aria-label="Limpiar búsqueda" onClick={() => setSearch('')} style={{
                position: 'absolute', right: 6, top: '50%', transform: 'translateY(-50%)',
                width: 28, height: 28, borderRadius: '50%', border: 'none', background: p.line,
                color: p.inkSoft, cursor: 'pointer', fontSize: 15, lineHeight: 1, display: 'grid', placeItems: 'center', padding: 0,
              }}>×</button>
            )}
          </div>

          {/* Filtros con contador */}
          <div className="foro-chips" role="tablist" aria-label="Filtrar publicaciones" style={{ padding: '10px 0 12px', margin: '0 -16px', paddingLeft: 16, paddingRight: 16 }}>
            {chips.map(f => {
              const active = filter === f.id;
              return (
                <button key={f.id} type="button" role="tab" aria-selected={active} className="foro-tap"
                  onClick={() => setFilter(f.id)} style={{
                    flexShrink: 0, height: 32, padding: '0 11px', borderRadius: 999, cursor: 'pointer',
                    border: `1px solid ${active ? p.primaryDeep : p.line}`,
                    background: active ? p.primaryDeep : p.surface, color: active ? '#fff' : p.inkSoft,
                    fontSize: 12.5, fontWeight: 600, display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: 'inherit',
                  }}>
                  {f.label}
                  {!loading && (
                    <span style={{
                      minWidth: 17, height: 17, padding: '0 4px', borderRadius: 999, fontSize: 10.5, fontWeight: 700,
                      display: 'inline-grid', placeItems: 'center',
                      background: active ? 'rgba(255,255,255,0.22)' : p.line, color: active ? '#fff' : p.inkSoft,
                    }}>{counts[f.id]}</span>
                  )}
                </button>
              );
            })}
          </div>
        </div>
      </div>

      {/* Feed */}
      <div ref={feedRef} data-foro="feed" onScroll={e => { feedScroll.current = e.currentTarget.scrollTop; }}
        style={{ flex: 1, overflowY: 'auto', overflowX: 'hidden', padding: '12px 16px', paddingBottom: 'calc(96px + var(--ser-nav-pad, 24px))' }}>
        <div style={{ ...inner, display: 'flex', flexDirection: 'column', gap: 12 }}>
          {!loading && <ForoGuide p={p} />}
          {body}
        </div>
      </div>

      {/* Aviso breve */}
      {toast && (
        <div role="status" className="foro-motion" style={{
          position: 'absolute', left: 16, right: 16, bottom: 'calc(84px + var(--ser-nav-pad, 24px))', zIndex: 45,
          display: 'flex', justifyContent: 'center', pointerEvents: 'none', animation: 'foroFadeUp .22s ease',
        }}>
          <div style={{ background: p.ink, color: p.bg, padding: '9px 16px', borderRadius: 999, fontSize: 13, fontWeight: 500, boxShadow: '0 8px 24px rgba(20,30,25,0.18)' }}>{toast}</div>
        </div>
      )}

      {/* Barra para escribir, fija abajo */}
      <div style={{
        position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 30,
        padding: '10px 16px', paddingBottom: 'calc(10px + var(--ser-nav-pad, 24px))',
        background: p.bg, borderTop: `1px solid ${p.line}`,
      }}>
        <div style={inner}>
          {userId ? (
            <button type="button" className="foro-tap" onClick={() => openComposer()} aria-label="Escribir una publicación" style={{
              width: '100%', display: 'flex', alignItems: 'center', gap: 10,
              padding: '7px 7px 7px 8px', borderRadius: 999,
              background: p.surface, border: `1px solid ${p.line}`, cursor: 'pointer', textAlign: 'left', fontFamily: 'inherit',
              boxShadow: p.dark ? 'none' : '0 2px 10px rgba(44,58,46,0.06)',
            }}>
              <ForoAvatar p={p} name={user?.name || 'Tú'} size={32} />
              <span style={{ flex: 1, fontSize: 14, color: p.inkMute, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                {firstName ? `¿Qué quieres compartir hoy, ${firstName}?` : '¿Qué quieres compartir hoy?'}
              </span>
              <span aria-hidden="true" style={{ width: 34, height: 34, borderRadius: '50%', background: p.primaryDeep, color: '#fff', display: 'grid', placeItems: 'center', flexShrink: 0 }}>
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
                  <path d="M12 20h9" /><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" />
                </svg>
              </span>
            </button>
          ) : (
            <div style={{ textAlign: 'center', fontSize: 13, color: p.inkMute, padding: '8px 0' }}>Inicia sesión para participar en el foro</div>
          )}
        </div>
      </div>

      {showComposer && (
        <ForoComposer p={p} user={user} initialType={composerType}
          onClose={() => setShowComposer(false)} onPublish={handlePublish} />
      )}
    </div>
  );
}

// ── Compositor (hoja inferior) ────────────────────────────────────────────────
function ForoComposer({ p, user, onClose, onPublish, initialType = 'desahogo' }) {
  const [text, setText] = React.useState('');
  const [type, setType] = React.useState(initialType === 'pregunta' ? 'pregunta' : 'desahogo');
  const [anon, setAnon] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState('');
  const [hint, setHint] = React.useState('');
  const taRef = React.useRef(null);
  const VB = window.VoiceButton;

  const trimmed = text.trim();
  const len = text.length;
  const nearLimit = len >= FORO_MAX_POST * 0.9;
  const canSend = trimmed.length > 0 && !busy;

  React.useEffect(() => {
    const t = setTimeout(() => { try { taRef.current?.focus(); } catch (_) {} }, 260);
    const onKey = (e) => { if (e.key === 'Escape' && !text.trim()) onClose(); };
    document.addEventListener('keydown', onKey);
    return () => { clearTimeout(t); document.removeEventListener('keydown', onKey); };
  }, []);

  const handleSend = async () => {
    if (!canSend) return;
    if (trimmed.length < 3) { setHint('Escribe un poco más para que la comunidad pueda acompañarte.'); taRef.current?.focus(); return; }
    setHint(''); setError(''); setBusy(true);
    try {
      await onPublish({ text: trimmed, anonymous: anon, post_type: type });
    } catch (e) {
      console.error('Error publicando en foro:', e);
      setError('No se pudo publicar. Revisa tu conexión e inténtalo de nuevo.');
      setBusy(false);
    }
  };
  const tryClose = () => { if (!busy) onClose(); };

  return (
    <div onClick={() => { if (!trimmed) tryClose(); }} className="foro-motion" style={{
      position: 'absolute', inset: 0, zIndex: 60, background: 'rgba(20,30,25,0.42)',
      display: 'flex', alignItems: 'flex-end', justifyContent: 'center', animation: 'foroFadeIn .2s ease',
    }}>
      <div role="dialog" aria-modal="true" aria-labelledby="foro-composer-title" onClick={e => e.stopPropagation()} style={{
        width: '100%', maxWidth: FORO_MAX_W, maxHeight: '92%', background: p.bg, color: p.ink,
        borderRadius: '24px 24px 0 0', display: 'flex', flexDirection: 'column',
        boxShadow: '0 -12px 40px rgba(20,30,25,0.18)', animation: 'foroSheetUp .28s cubic-bezier(.2,.8,.2,1)',
      }}>
        <div style={{ width: 36, height: 4, borderRadius: 2, background: p.line, margin: '10px auto 0', flexShrink: 0 }} />

        {/* Cabecera de la hoja */}
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 16px 10px', flexShrink: 0 }}>
          <button type="button" onClick={tryClose} className="foro-tap" style={{
            background: 'transparent', border: 'none', cursor: 'pointer', color: p.inkSoft,
            fontSize: 14.5, fontWeight: 500, padding: '6px 0', fontFamily: 'inherit', minWidth: 72, textAlign: 'left',
          }}>{trimmed ? 'Descartar' : 'Cancelar'}</button>
          <div id="foro-composer-title" style={{ fontFamily: 'Fraunces, serif', fontSize: 17, fontWeight: 500, color: p.ink }}>Nueva publicación</div>
          <button type="button" onClick={handleSend} disabled={!canSend} className="foro-tap" aria-busy={busy} style={{
            minWidth: 88, padding: '8px 16px', borderRadius: 999, border: 'none', fontSize: 14, fontWeight: 600, fontFamily: 'inherit',
            background: canSend ? p.primaryDeep : p.line, color: canSend ? '#fff' : p.inkMute,
            cursor: canSend ? 'pointer' : 'default', transition: 'background-color .15s ease, color .15s ease',
          }}>{busy ? 'Publicando…' : 'Publicar'}</button>
        </div>

        <div style={{ overflowY: 'auto', padding: '4px 16px', paddingBottom: 'calc(18px + var(--ser-nav-pad, 24px))', display: 'flex', flexDirection: 'column', gap: 14 }}>
          {/* Tipo */}
          <div role="radiogroup" aria-label="Tipo de publicación" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            {['pregunta', 'desahogo'].map(id => {
              const on = type === id;
              const color = id === 'pregunta' ? p.shellBlue : p.primaryDeep;
              return (
                <button key={id} type="button" role="radio" aria-checked={on} className="foro-tap" onClick={() => setType(id)} style={{
                  padding: '10px 12px', borderRadius: 16, textAlign: 'left', cursor: 'pointer', fontFamily: 'inherit',
                  border: `1.5px solid ${on ? color : p.line}`, background: on ? color + '14' : p.surface,
                  transition: 'border-color .15s ease, background-color .15s ease',
                }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                    <span style={{ width: 8, height: 8, borderRadius: '50%', background: on ? color : p.line, flexShrink: 0 }} />
                    <span style={{ fontSize: 13.5, fontWeight: 600, color: on ? color : p.ink }}>{FORO_TYPES[id].label}</span>
                  </div>
                  <div style={{ fontSize: 11.5, color: p.inkSoft, marginTop: 3, lineHeight: 1.35 }}>{FORO_TYPES[id].desc}</div>
                </button>
              );
            })}
          </div>

          {/* Texto */}
          <div>
            <div style={{ position: 'relative' }}>
              <textarea ref={taRef} className="foro-ta" value={text} maxLength={FORO_MAX_POST}
                onChange={e => { setText(e.target.value); if (hint) setHint(''); }}
                aria-label={type === 'pregunta' ? 'Tu pregunta' : 'Tu desahogo'}
                placeholder={type === 'pregunta' ? '¿Qué te gustaría preguntarle a la comunidad?' : '¿Cómo estás hoy? Escribe con libertad…'}
                rows={6}
                style={{
                  '--foro-ph': p.inkMute, width: '100%', boxSizing: 'border-box', padding: '14px 14px 40px', borderRadius: 16,
                  border: `1.5px solid ${trimmed ? p.primaryDeep + '88' : p.line}`, background: p.surface, color: p.ink,
                  fontSize: 16, lineHeight: 1.6, fontFamily: 'Fraunces, serif', resize: 'none', minHeight: 150,
                  transition: 'border-color .15s ease',
                }} />
              <div style={{ position: 'absolute', right: 10, bottom: 10, display: 'flex', alignItems: 'center', gap: 8 }}>
                <span aria-live="polite" style={{ fontSize: 11.5, fontWeight: 600, color: nearLimit ? '#B7791F' : p.inkMute, fontVariantNumeric: 'tabular-nums' }}>
                  {len}/{FORO_MAX_POST}
                </span>
                {VB ? <VB p={p} getValue={() => text} onFull={setText} /> : null}
              </div>
            </div>
            {hint && <div role="status" style={{ fontSize: 12.5, color: p.dark ? p.accent : '#8A6B2F', marginTop: 8, lineHeight: 1.45 }}>{hint}</div>}
            {error && <div role="alert" style={{ fontSize: 12.5, color: '#C5524B', marginTop: 8, lineHeight: 1.45 }}>{error}</div>}
          </div>

          {/* Identidad */}
          <button type="button" role="switch" aria-checked={anon} onClick={() => setAnon(a => !a)} className="foro-tap" style={{
            display: 'flex', alignItems: 'center', gap: 10, padding: '10px 12px', borderRadius: 16, cursor: 'pointer',
            background: p.surface, border: `1px solid ${p.line}`, textAlign: 'left', fontFamily: 'inherit', width: '100%',
          }}>
            <ForoAvatar p={p} name={user?.name || 'Tú'} anon={anon} type={type} size={34} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13.5, fontWeight: 600, color: p.ink }}>{anon ? 'Publicar como Anónimo' : `Publicar como ${user?.name || 'tú'}`}</div>
              <div style={{ fontSize: 11.5, color: p.inkMute, marginTop: 1 }}>{anon ? 'Nadie verá tu nombre' : 'Tu nombre será visible para la comunidad'}</div>
            </div>
            <span aria-hidden="true" style={{
              width: 42, height: 26, borderRadius: 999, flexShrink: 0, position: 'relative',
              background: anon ? p.primaryDeep : p.line, transition: 'background-color .18s ease',
            }}>
              <span style={{
                position: 'absolute', top: 3, left: anon ? 19 : 3, width: 20, height: 20, borderRadius: '50%',
                background: '#fff', boxShadow: '0 1px 3px rgba(0,0,0,0.25)', transition: 'left .18s ease',
              }} />
            </span>
          </button>

          <div style={{ fontSize: 12, color: p.inkMute, lineHeight: 1.5, textAlign: 'center' }}>
            Aquí escuchamos sin diagnosticar. Si estás en riesgo, usa el botón SOS de Serenia.
          </div>
        </div>
      </div>
    </div>
  );
}

// ── Detalle de la publicación + hilo ─────────────────────────────────────────
function ForoPostDetail({ p, post, userId, user, onBack, onReact, onDelete, onReplyCountLoaded, onReplied }) {
  const type = foroType(post);
  const isOwn = !!userId && post.user_id === userId;
  const [replies, setReplies] = React.useState([]);
  const [repliesLoading, setRepliesLoading] = React.useState(true);
  const [replyText, setReplyText] = React.useState('');
  const [replyAnon, setReplyAnon] = React.useState(false);
  const [sendingReply, setSendingReply] = React.useState(false);
  const [replyError, setReplyError] = React.useState('');
  const scrollRef = React.useRef(null);
  const taRef = React.useRef(null);
  const T = window.Turtle;
  const inner = { maxWidth: FORO_MAX_W, margin: '0 auto', width: '100%' };

  const loadReplies = React.useCallback(() => {
    if (!window.SupaForo) return Promise.resolve([]);
    return window.SupaForo.getReplies(post.id).then(data => {
      const loaded = data || [];
      setReplies(loaded);
      if (onReplyCountLoaded) onReplyCountLoaded(post.id, loaded.length);
      return loaded;
    });
  }, [post.id]);

  React.useEffect(() => {
    if (!window.SupaForo) { setRepliesLoading(false); return; }
    setRepliesLoading(true);
    loadReplies().catch(() => {}).finally(() => setRepliesLoading(false));
  }, [post.id]);

  // Tiempo real del hilo: respuestas de otras personas
  React.useEffect(() => {
    const db = foroDb();
    if (!db) return;
    const channel = db.channel('foro-hilo-' + post.id)
      .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'foro_replies', filter: `post_id=eq.${post.id}` }, ({ new: row }) => {
        if (!row?.id || row.user_id === userId) return;
        loadReplies().catch(() => {}); // recarga para traer el nombre del autor
      })
      .on('postgres_changes', { event: 'DELETE', schema: 'public', table: 'foro_replies' }, ({ old }) => {
        if (!old?.id) return;
        setReplies(prev => prev.filter(r => r.id !== old.id));
      })
      .subscribe();
    return () => { try { db.removeChannel(channel); } catch (_) {} };
  }, [post.id, userId]);

  const scrollToEnd = () => {
    requestAnimationFrame(() => {
      const el = scrollRef.current;
      if (el) el.scrollTo({ top: el.scrollHeight, behavior: foroReducedMotion() ? 'auto' : 'smooth' });
    });
  };
  const autosize = (el) => { if (!el) return; el.style.height = 'auto'; el.style.height = Math.min(el.scrollHeight, 112) + 'px'; };

  const sendReply = async () => {
    const trimmed = replyText.trim();
    if (!trimmed || !userId || sendingReply) return;
    setSendingReply(true); setReplyError('');
    const optimistic = {
      id: 'r_' + Date.now(), post_id: post.id, user_id: userId,
      author: { name: replyAnon ? null : (user?.name || 'Tú') },
      anonymous: replyAnon, text: trimmed, created_at: new Date().toISOString(), _pending: true,
    };
    setReplies(prev => [...prev, optimistic]);
    setReplyText('');
    if (taRef.current) taRef.current.style.height = ''; // vuelve a una línea (React vacía el valor en el siguiente render)
    if (onReplied) onReplied(post.id, 1);
    scrollToEnd();
    try {
      if (window.SupaForo) {
        const saved = await window.SupaForo.addReply(post.id, userId, { text: trimmed, anonymous: replyAnon });
        setReplies(prev => prev.map(r => r.id === optimistic.id ? saved : r));
      }
      window.haptic?.('light');
      // Notificación al autor de la publicación
      if (window.sereniaNotifAdd && post.user_id && post.user_id !== userId) {
        const authorName = replyAnon ? 'Alguien' : (user?.name || 'Alguien');
        window.sereniaNotifAdd(post.user_id, {
          type: 'forum_reply',
          title: 'Nueva respuesta en tu publicación',
          body: `${authorName}: "${trimmed.slice(0, 70)}${trimmed.length > 70 ? '…' : ''}"`,
          data: { post_id: post.id },
        });
      }
    } catch (err) {
      console.error('Foro addReply error:', err);
      setReplies(prev => prev.filter(r => r.id !== optimistic.id));
      if (onReplied) onReplied(post.id, -1);
      setReplyText(trimmed);
      setReplyError('No se pudo enviar tu respuesta. Inténtalo de nuevo.');
    }
    setSendingReply(false);
  };

  const deleteReply = async (r) => {
    if (!window.confirm('¿Eliminar esta respuesta?')) return;
    try {
      await window.SupaForo.deleteReply(r.id, userId);
      setReplies(rs => rs.filter(x => x.id !== r.id));
      if (onReplied) onReplied(post.id, -1);
    } catch (_) { alert('No se pudo eliminar la respuesta.'); }
  };

  const replyName = replyAnon ? 'Anónimo' : ((user?.name || '').trim().split(/\s+/)[0] || 'tú');
  const canSendReply = !!replyText.trim() && !sendingReply;

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: p.bg, color: p.ink }}>
      {/* Cabecera */}
      <div style={{ padding: '10px 16px', borderBottom: `1px solid ${p.line}`, flexShrink: 0, background: p.bg }}>
        <div style={{ ...inner, display: 'flex', alignItems: 'center', justifyContent: 'space-between', minHeight: 32 }}>
          <button type="button" onClick={onBack} className="foro-tap" style={{
            background: 'transparent', border: 'none', cursor: 'pointer', color: p.primaryDeep,
            fontSize: 15, fontWeight: 600, padding: '4px 0', fontFamily: 'inherit', minWidth: 64, textAlign: 'left',
          }}>‹ Foro</button>
          <div style={{ fontFamily: 'Fraunces, serif', fontSize: 17, fontWeight: 500, color: p.ink }}>{FORO_TYPES[type].label}</div>
          <div style={{ minWidth: 64, display: 'flex', justifyContent: 'flex-end' }}>
            {isOwn && onDelete && (
              <ForoMenu p={p} items={[{ label: 'Eliminar publicación', danger: true, onClick: () => { if (window.confirm('¿Eliminar esta publicación? No se puede deshacer.')) onDelete(); } }]} label="Opciones de tu publicación" />
            )}
          </div>
        </div>
      </div>

      {/* Publicación + hilo */}
      <div ref={scrollRef} data-foro="hilo" style={{ flex: 1, overflowY: 'auto', overflowX: 'hidden', padding: '14px 16px 18px' }}>
        <div style={inner}>
          <ForoCard p={p} post={post} userId={userId} expanded onReact={onReact} />

          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, margin: '20px 2px 10px' }}>
            <div style={{ fontFamily: 'Fraunces, serif', fontSize: 17, fontWeight: 500, color: p.ink }}>Respuestas</div>
            {!repliesLoading && replies.length > 0 && <div style={{ fontSize: 12.5, color: p.inkMute }}>{replies.length}</div>}
          </div>

          {repliesLoading && (
            <div className="foro-motion" aria-busy="true" style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              {[0, 1].map(i => (
                <div key={i} style={{ display: 'flex', gap: 10, padding: '4px 2px' }}>
                  <div style={{ width: 30, height: 30, borderRadius: '50%', background: p.line, flexShrink: 0 }} />
                  <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 7, paddingTop: 4 }}>
                    <div style={{ width: '34%', height: 10, borderRadius: 999, background: `linear-gradient(90deg, ${p.line}88 25%, ${p.line} 50%, ${p.line}88 75%)`, backgroundSize: '200% 100%', animation: 'foroShimmer 1.4s linear infinite' }} />
                    <div style={{ width: '88%', height: 12, borderRadius: 999, background: `linear-gradient(90deg, ${p.line}88 25%, ${p.line} 50%, ${p.line}88 75%)`, backgroundSize: '200% 100%', animation: 'foroShimmer 1.4s linear infinite' }} />
                  </div>
                </div>
              ))}
            </div>
          )}

          {!repliesLoading && replies.length === 0 && (
            <div className="foro-motion" style={{
              display: 'flex', alignItems: 'center', gap: 14, padding: '14px 16px', borderRadius: 18,
              background: p.surface, border: `1px dashed ${p.line}`, animation: 'foroFadeIn .25s ease',
            }}>
              {T ? <T pose="chat" size={64} animate /> : <span style={{ fontSize: 36 }}>🐢</span>}
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontFamily: 'Fraunces, serif', fontSize: 16, fontWeight: 500, color: p.ink, lineHeight: 1.25 }}>
                  {type === 'pregunta' ? 'Sé la primera persona en responder' : 'Nadie ha respondido todavía'}
                </div>
                <div style={{ fontSize: 12.5, color: p.inkSoft, marginTop: 4, lineHeight: 1.5 }}>
                  {type === 'pregunta' ? 'Si pasaste por algo parecido, contarlo puede ayudar mucho.' : 'Un par de líneas pueden acompañar más de lo que crees.'}
                </div>
              </div>
            </div>
          )}

          {!repliesLoading && replies.length > 0 && (
            <ol style={{ listStyle: 'none', margin: 0, padding: 0, display: 'flex', flexDirection: 'column', gap: 6 }}>
              {replies.map((r, i) => {
                const rAnon = !!r.anonymous;
                const rName = rAnon ? 'Anónimo' : (r.author?.name || r.author_name || 'Alguien de Serenia');
                const isOwnReply = !!userId && r.user_id === userId;
                const isAuthor = !rAnon && !post.anonymous && r.user_id === post.user_id;
                return (
                  <li key={r.id} className="foro-motion" style={{
                    display: 'flex', gap: 10, padding: '10px 12px', borderRadius: 18,
                    background: isOwnReply ? p.primarySoft + '55' : p.surface, border: `1px solid ${isOwnReply ? p.primarySoft : p.line}`,
                    opacity: r._pending ? 0.7 : 1, animation: i >= replies.length - 3 ? 'foroFadeUp .22s ease both' : 'none',
                  }}>
                    <ForoAvatar p={p} name={rName} anon={rAnon} type={type} size={30} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }}>
                        <span style={{ fontSize: 13, fontWeight: 600, color: p.ink, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{rName}</span>
                        {isAuthor && <span style={{ fontSize: 10.5, fontWeight: 700, color: p.primaryDeep, background: p.primarySoft, padding: '1px 6px', borderRadius: 999, flexShrink: 0 }}>Autor</span>}
                        <time dateTime={r.created_at} title={foroFullDate(r.created_at)} style={{ fontSize: 11, color: p.inkMute, marginLeft: 'auto', flexShrink: 0 }}>
                          {r._pending ? 'enviando…' : foroTimeAgo(r.created_at)}
                        </time>
                        {isOwnReply && !r._pending && (
                          <ForoMenu p={p} size={26} label="Opciones de tu respuesta" items={[{ label: 'Eliminar respuesta', danger: true, onClick: () => deleteReply(r) }]} />
                        )}
                      </div>
                      <div style={{ fontSize: 14, color: p.ink, lineHeight: 1.55, marginTop: 3, whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }}>{r.text}</div>
                    </div>
                  </li>
                );
              })}
            </ol>
          )}
        </div>
      </div>

      {/* Campo de respuesta, fijo abajo (en flujo: nunca tapa el último mensaje) */}
      {userId ? (
        <div style={{ flexShrink: 0, borderTop: `1px solid ${p.line}`, background: p.bg, padding: '10px 16px', paddingBottom: 'calc(10px + var(--ser-nav-pad, 24px))' }}>
          <div style={inner}>
            <div style={{ display: 'flex', alignItems: 'flex-end', gap: 8 }}>
              <div style={{
                flex: 1, minWidth: 0, background: p.surface, borderRadius: 20, padding: '8px 14px',
                border: `1.5px solid ${replyText.trim() ? p.primaryDeep + '88' : p.line}`, transition: 'border-color .15s ease',
              }}>
                <textarea ref={taRef} className="foro-ta" value={replyText} rows={1} maxLength={FORO_MAX_REPLY}
                  aria-label="Escribe una respuesta"
                  onChange={e => { setReplyText(e.target.value); autosize(e.target); if (replyError) setReplyError(''); }}
                  onKeyDown={e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendReply(); } }}
                  placeholder={type === 'pregunta' ? 'Comparte lo que sabes o viviste…' : 'Escribe unas palabras de apoyo…'}
                  style={{
                    '--foro-ph': p.inkMute, width: '100%', border: 'none', background: 'transparent', color: p.ink,
                    fontSize: 15, lineHeight: 1.5, fontFamily: 'inherit', resize: 'none', boxSizing: 'border-box',
                    display: 'block', maxHeight: 112, overflowY: 'auto', padding: 0,
                  }} />
              </div>
              <button type="button" onClick={sendReply} disabled={!canSendReply} aria-label="Enviar respuesta" className="foro-tap" style={{
                width: 40, height: 40, borderRadius: '50%', border: 'none', flexShrink: 0,
                background: canSendReply ? p.primaryDeep : p.line, color: canSendReply ? '#fff' : p.inkMute,
                cursor: canSendReply ? 'pointer' : 'default', display: 'grid', placeItems: 'center',
                transition: 'background-color .15s ease',
              }}>
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                  <path d="M12 19V5" /><path d="M5 12l7-7 7 7" />
                </svg>
              </button>
            </div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 6, paddingLeft: 4, fontSize: 11.5, color: p.inkMute, minHeight: 18, flexWrap: 'wrap' }}>
              {replyError ? (
                <span role="alert" style={{ color: '#C5524B' }}>{replyError}</span>
              ) : (
                <>
                  <span>Respondes como <b style={{ fontWeight: 600, color: p.inkSoft }}>{replyName}</b></span>
                  <span aria-hidden="true">·</span>
                  <button type="button" onClick={() => setReplyAnon(a => !a)} style={{
                    background: 'transparent', border: 'none', padding: 0, cursor: 'pointer',
                    color: p.primaryDeep, fontSize: 11.5, fontWeight: 600, fontFamily: 'inherit',
                  }}>{replyAnon ? 'Usar mi nombre' : 'Cambiar a Anónimo'}</button>
                </>
              )}
            </div>
          </div>
        </div>
      ) : (
        <div style={{ flexShrink: 0, borderTop: `1px solid ${p.line}`, padding: '14px 18px', paddingBottom: 'calc(14px + var(--ser-nav-pad, 24px))', textAlign: 'center', color: p.inkMute, fontSize: 13 }}>
          Inicia sesión para responder
        </div>
      )}
    </div>
  );
}
