/* Portal O-Lab — raíz. Auth gate → login/registro (rol) → app aplicante/empresa. */
(function () {
  const { useState, useEffect } = React;
  const { C, FONT, Icon, Btn, useIsMobile } = window.PORTAL_UI;
  const B = () => window.PORTAL_BACKEND;

  function Splash() {
    return (
      <div style={{ minHeight: "100vh", background: C.feedBg, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: FONT }}>
        <div style={{ color: "rgba(255,255,255,0.7)", fontWeight: 700, fontSize: 14 }}>Cargando O-Lab…</div>
      </div>
    );
  }

  function friendlyErr(err) {
    const c = (err && err.code) || "";
    if (c.includes("email-already-in-use")) return "Ese correo ya tiene una cuenta. Inicia sesión.";
    if (c.includes("invalid-credential") || c.includes("wrong-password") || c.includes("user-not-found")) return "Correo o contraseña incorrectos.";
    if (c.includes("invalid-email")) return "El correo no es válido.";
    if (c.includes("weak-password")) return "La contraseña debe tener al menos 6 caracteres.";
    if (c.includes("network")) return "Sin conexión con Firebase. Revisa tu internet.";
    return (err && err.message) || "Ocurrió un error. Intenta de nuevo.";
  }

  const TERMS_UPDATED = "2 de septiembre de 2026";
  // Documento formal de Términos, Condiciones y Política de Tratamiento de Datos.
  // Estructurado en secciones para que se lea como un documento legal completo.
  const TERMS_SECTIONS = [
    {
      h: "1. Identificación del responsable",
      p: "El presente Portal de Talento O-Lab (en adelante, el \"Portal\") es operado por O-Lab en alianza con la Fundación El Origen (en adelante, conjuntamente, \"O-Lab\", \"nosotros\" o el \"Responsable\"). Para todos los efectos relacionados con la protección de datos personales, el Responsable puede ser contactado en el correo electrónico hola@o-lab.ai. Estos Términos y Condiciones y la Política de Tratamiento de Datos Personales (en adelante, los \"Términos\") regulan el acceso y uso del Portal por parte de personas naturales que se registran como aspirantes o talentos (\"Aplicantes\") y de personas jurídicas o sus representantes que se registran como empresas (\"Empresas\"). En adelante, Aplicantes y Empresas se denominan indistintamente el \"Usuario\".",
    },
    {
      h: "2. Aceptación de los Términos",
      p: "Al crear una cuenta, solicitar acceso, iniciar sesión o utilizar de cualquier forma el Portal, el Usuario declara que ha leído, entendido y aceptado íntegramente estos Términos, así como la Política de Tratamiento de Datos Personales aquí incorporada. Si el Usuario no está de acuerdo con cualquiera de las disposiciones, debe abstenerse de registrarse y de utilizar el Portal. La aceptación se manifiesta de forma expresa mediante la casilla de verificación dispuesta para el efecto y de forma inequívoca mediante el uso continuado del Portal.",
    },
    {
      h: "3. Objeto del Portal",
      p: "El Portal es una plataforma tecnológica de empleabilidad y desarrollo de talento que permite a los Aplicantes construir un perfil profesional, participar en laboratorios de aprendizaje (\"Labs\"), pruebas y evaluaciones, acceder a contenidos formativos, postularse a vacantes y ser visibles ante Empresas aliadas; y permite a las Empresas publicar vacantes, descubrir talento y gestionar procesos de selección. O-Lab actúa como intermediario tecnológico y no garantiza la obtención de un empleo, contrato, remuneración o resultado específico derivado del uso del Portal.",
    },
    {
      h: "4. Registro, cuenta y veracidad de la información",
      p: "El registro requiere información veraz, exacta, completa y actualizada. El Usuario es el único responsable de mantener la confidencialidad de sus credenciales de acceso y de todas las actividades realizadas bajo su cuenta. El Usuario se compromete a notificar de inmediato cualquier uso no autorizado de su cuenta. O-Lab podrá suspender o cancelar cuentas que contengan información falsa, desactualizada, que suplanten a terceros o que incumplan estos Términos. El acceso al Portal puede estar sujeto a aprobación previa y a un período de vigencia determinado por O-Lab.",
    },
    {
      h: "5. Tratamiento de datos personales (habeas data)",
      p: "En cumplimiento de la Ley 1581 de 2012, el Decreto 1074 de 2015 y demás normas concordantes de la República de Colombia sobre protección de datos personales, el Usuario, actuando como titular, autoriza de manera previa, expresa e informada a O-Lab para recolectar, almacenar, usar, circular, actualizar y suprimir sus datos personales conforme a las finalidades descritas en estos Términos. El tratamiento se realiza bajo los principios de legalidad, finalidad, libertad, veracidad, transparencia, acceso y circulación restringida, seguridad y confidencialidad.",
    },
    {
      h: "6. Finalidades del tratamiento",
      p: "Los datos personales del Usuario serán tratados para: (i) gestionar el registro, la autenticación y la administración de la cuenta; (ii) habilitar la participación en Labs, pruebas, retos, eventos y demás actividades del Portal; (iii) construir y mostrar el perfil profesional y la hoja de vida del Aplicante; (iv) conectar a los Aplicantes con oportunidades laborales y con Empresas aliadas; (v) permitir a las Empresas evaluar candidatos y gestionar procesos de selección; (vi) enviar comunicaciones operativas, formativas e informativas relacionadas con el Portal; (vii) elaborar estadísticas, indicadores de impacto y reportes agregados y anonimizados; y (viii) cumplir obligaciones legales y requerimientos de autoridades competentes.",
    },
    {
      h: "7. Datos sensibles y de menores",
      p: "Algunos datos suministrados voluntariamente por el Usuario (por ejemplo, fotografías, información socioeconómica o de diagnóstico) podrán tener el carácter de sensibles. El suministro de datos sensibles es facultativo y el Usuario no está obligado a autorizar su tratamiento. Tratándose de menores de edad, el tratamiento solo procede en su interés superior y con la autorización previa del padre, madre o representante legal, quien declara contar con la facultad para otorgarla. Al aceptar estos Términos, el Aplicante declara ser mayor de edad o contar con la autorización de su acudiente o representante legal.",
    },
    {
      h: "8. Visibilidad del perfil ante Empresas",
      p: "El Aplicante entiende y autoriza que su perfil profesional —incluyendo, entre otros, su nombre, fotografía, competencias, formación, experiencia y hoja de vida— podrá ser visible para las Empresas registradas en el Portal con el propósito de facilitar procesos de empleabilidad. El Usuario podrá gestionar, dentro de las opciones disponibles, el alcance de dicha visibilidad. La información de contacto directa se compartirá con las Empresas conforme a los flujos previstos en el Portal.",
    },
    {
      h: "9. Obligaciones y conducta del Usuario",
      p: "El Usuario se obliga a utilizar el Portal de forma lícita, diligente y de buena fe, y se abstendrá de: (i) publicar contenido falso, difamatorio, discriminatorio, ofensivo o que infrinja derechos de terceros; (ii) suplantar la identidad de personas u organizaciones; (iii) recolectar datos de otros usuarios sin autorización; (iv) introducir software malicioso o vulnerar la seguridad del Portal; (v) utilizar el Portal con fines distintos a los previstos, incluida cualquier finalidad comercial no autorizada; y (vi) infringir la ley aplicable. O-Lab podrá retirar contenidos y suspender cuentas que incumplan lo anterior.",
    },
    {
      h: "10. Uso de inteligencia artificial",
      p: "El Portal puede incorporar funcionalidades asistidas por inteligencia artificial (por ejemplo, sugerencias para mejorar textos de la hoja de vida). Dichas funcionalidades son de apoyo, pueden contener imprecisiones y no sustituyen el criterio del Usuario, quien es responsable de revisar y validar el contenido final de su perfil. Determinadas herramientas de IA pueden estar habilitadas únicamente para perfiles administradores.",
    },
    {
      h: "11. Propiedad intelectual",
      p: "El Portal, su marca, logotipos, diseño, código, contenidos y demás elementos son propiedad de O-Lab o de sus licenciantes y están protegidos por las normas de propiedad intelectual. El Usuario conserva la titularidad sobre los contenidos que aporta (por ejemplo, su hoja de vida) y, por el hecho de publicarlos, otorga a O-Lab una licencia no exclusiva, gratuita y limitada para alojarlos, reproducirlos y mostrarlos dentro del Portal con el fin de prestar el servicio.",
    },
    {
      h: "12. Seguridad de la información",
      p: "O-Lab adopta medidas técnicas, humanas y administrativas razonables para proteger los datos personales frente a accesos no autorizados, pérdida, alteración o uso indebido. No obstante, ningún sistema es completamente seguro; el Usuario reconoce que el tratamiento de la información a través de internet conlleva riesgos inherentes que no pueden eliminarse por completo.",
    },
    {
      h: "13. Transferencia y transmisión de datos",
      p: "Para la operación del Portal, O-Lab podrá apoyarse en proveedores de servicios tecnológicos (por ejemplo, servicios de infraestructura en la nube), quienes actúan como encargados del tratamiento bajo obligaciones de confidencialidad y seguridad. Algunos de estos proveedores pueden operar servidores fuera del país; al aceptar estos Términos, el Usuario autoriza dichas transmisiones y transferencias internacionales de datos, las cuales se realizarán observando las garantías exigidas por la ley aplicable.",
    },
    {
      h: "14. Derechos del titular",
      p: "El Usuario, como titular de sus datos, tiene derecho a conocer, actualizar, rectificar y suprimir sus datos personales, así como a solicitar prueba de la autorización otorgada, a ser informado sobre el uso dado a sus datos, a presentar quejas ante la autoridad competente y a revocar la autorización cuando ello proceda. Estos derechos pueden ejercerse enviando una solicitud al correo hola@o-lab.ai, indicando su identificación y la petición concreta. O-Lab atenderá las solicitudes dentro de los términos legales.",
    },
    {
      h: "15. Conservación de los datos",
      p: "Los datos personales se conservarán mientras la cuenta del Usuario permanezca activa y durante el tiempo necesario para cumplir las finalidades del tratamiento y las obligaciones legales aplicables. Una vez cesen dichas finalidades, los datos serán suprimidos o anonimizados, salvo obligación legal de conservación.",
    },
    {
      h: "16. Limitación de responsabilidad",
      p: "El Portal se ofrece \"tal cual\" y \"según disponibilidad\". En la máxima medida permitida por la ley, O-Lab no será responsable por: (i) las decisiones de contratación o selección adoptadas por las Empresas; (ii) la veracidad de la información publicada por los Usuarios; (iii) interrupciones, fallos o indisponibilidad temporal del servicio; ni (iv) daños indirectos, incidentales o lucro cesante derivados del uso o imposibilidad de uso del Portal. Las relaciones laborales o contractuales que surjan entre Aplicantes y Empresas se rigen exclusivamente por los acuerdos que estos celebren y por la ley aplicable, sin responsabilidad para O-Lab.",
    },
    {
      h: "17. Vigencia, suspensión y terminación",
      p: "Estos Términos rigen mientras el Usuario mantenga una cuenta activa en el Portal. O-Lab podrá suspender o cancelar el acceso, de forma temporal o definitiva, ante el incumplimiento de estos Términos, por razones de seguridad, por requerimiento legal o por decisión operativa debidamente informada. El Usuario podrá solicitar la terminación de su cuenta en cualquier momento a través de los canales dispuestos.",
    },
    {
      h: "18. Modificaciones",
      p: "O-Lab podrá modificar estos Términos para reflejar cambios legales, técnicos u operativos. Las modificaciones se informarán a través del Portal y entrarán en vigencia desde su publicación. El uso continuado del Portal con posterioridad a la publicación de los cambios implica la aceptación de la versión vigente.",
    },
    {
      h: "19. Ley aplicable y jurisdicción",
      p: "Estos Términos se rigen e interpretan conforme a las leyes de la República de Colombia. Cualquier controversia que surja en relación con su interpretación, ejecución o cumplimiento se someterá a la jurisdicción de los jueces y tribunales competentes del domicilio del Responsable, sin perjuicio de los mecanismos alternativos de solución de conflictos que las partes acuerden.",
    },
    {
      h: "20. Contacto",
      p: "Para ejercer sus derechos, presentar consultas o reclamos sobre el tratamiento de sus datos personales, o solicitar la política completa de tratamiento de datos, el Usuario puede escribir a hola@o-lab.ai. O-Lab dará respuesta en los términos y plazos previstos por la ley aplicable.",
    },
  ];
  function TermsModal({ onClose }) {
    return (
      <div onClick={onClose} style={{ position: "fixed", inset: 0, zIndex: 90, background: "rgba(23,19,31,0.6)", display: "flex", alignItems: "center", justifyContent: "center", padding: 20 }}>
        <div onClick={(e) => e.stopPropagation()} style={{ width: "100%", maxWidth: 620, background: "#fff", borderRadius: 20, padding: "24px 26px", animation: "olabRise .25s ease both", maxHeight: "88vh", overflowY: "auto" }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 4, position: "sticky", top: -24, background: "#fff", paddingTop: 4, paddingBottom: 8, zIndex: 1 }}>
            <div>
              <div style={{ fontSize: 20, fontWeight: 800, color: C.ink }}>Términos y Condiciones y Política de Tratamiento de Datos Personales</div>
              <div style={{ fontSize: 11.5, fontWeight: 600, color: C.mut3, marginTop: 4, textTransform: "uppercase", letterSpacing: ".04em" }}>Portal de Talento O-Lab · Fundación El Origen · Última actualización: {TERMS_UPDATED}</div>
            </div>
            <button onClick={onClose} style={{ border: `1px solid ${C.line2}`, background: "#fff", borderRadius: 999, width: 32, height: 32, minWidth: 32, cursor: "pointer", color: C.mut3, marginLeft: 12 }}><Icon name="close" size={16} /></button>
          </div>
          <div style={{ fontSize: 12.5, fontWeight: 500, color: C.mut2, lineHeight: 1.7, borderTop: `1px solid ${C.line2}`, paddingTop: 14 }}>
            <p style={{ margin: "0 0 16px", fontStyle: "italic", color: C.mut3 }}>
              Le recomendamos leer detenidamente el presente documento antes de registrarse o iniciar sesión en el Portal. Al aceptarlo, usted manifiesta su conocimiento y consentimiento respecto de las condiciones de uso y del tratamiento de sus datos personales.
            </p>
            {TERMS_SECTIONS.map((s) => (
              <div key={s.h} style={{ marginBottom: 16 }}>
                <div style={{ fontSize: 13.5, fontWeight: 800, color: C.ink, marginBottom: 5 }}>{s.h}</div>
                <div style={{ textAlign: "justify" }}>{s.p}</div>
              </div>
            ))}
            <p style={{ margin: "4px 0 0", fontWeight: 700, color: C.ink }}>
              Declaro que he leído y acepto en su totalidad los presentes Términos y Condiciones y la Política de Tratamiento de Datos Personales, y otorgo mi autorización libre, previa, expresa e informada para el tratamiento de mis datos conforme a lo aquí establecido.
            </p>
          </div>
          <button onClick={onClose} style={{ marginTop: 20, width: "100%", border: 0, background: C.ink, color: "#fff", fontFamily: FONT, fontSize: 14, fontWeight: 800, padding: 13, borderRadius: 13, cursor: "pointer" }}>Entendido</button>
        </div>
      </div>
    );
  }

  // Carrusel de fondo a pantalla completa (full-bleed) para la portada de entrada.
  // Cubre TODO el panel izquierdo y rota las fotos que existan en
  // assets/portada-1.jpg … portada-8.jpg (las que no existan se ignoran solas).
  function EntryCarousel() {
    const srcs = [1, 2, 3, 4, 5, 6, 7, 8].map((n) => "assets/portada-" + n + ".jpg?v=2");
    const [loaded, setLoaded] = useState([]);
    const [i, setI] = useState(0);
    useEffect(() => {
      let alive = true; const good = []; let pending = srcs.length;
      const done = () => { pending -= 1; if (pending === 0 && alive) setLoaded(good.slice()); };
      srcs.forEach((src) => { const im = new Image(); im.onload = () => { good.push(src); done(); }; im.onerror = done; im.src = src; });
      return () => { alive = false; };
    }, []);
    useEffect(() => {
      if (loaded.length < 2) return;
      const t = setInterval(() => setI((x) => (x + 1) % loaded.length), 4200);
      return () => clearInterval(t);
    }, [loaded.length]);
    if (!loaded.length) return null; // sin fotos → se ve solo el degradado de fondo
    const cur = i % loaded.length;
    return (
      <React.Fragment>
        {loaded.map((src, k) => (
          <div key={src} style={{ position: "absolute", inset: 0, backgroundImage: 'url("' + src + '")', backgroundSize: "cover", backgroundPosition: "50% 26%", opacity: k === cur ? 1 : 0, transform: k === cur ? "scale(1)" : "scale(1.08)", transition: "opacity 1.1s ease, transform 6s ease" }} />
        ))}
        {/* Scrim para que el texto siempre se lea sobre la foto */}
        <div style={{ position: "absolute", inset: 0, background: "linear-gradient(155deg, rgba(23,19,31,0.80) 0%, rgba(23,19,31,0.42) 44%, rgba(23,19,31,0.92) 100%)" }} />
        {loaded.length > 1 && (
          <div style={{ position: "absolute", left: 48, bottom: 66, zIndex: 3, display: "flex", gap: 8 }}>
            {loaded.map((_, k) => <span key={k} onClick={() => setI(k)} style={{ width: k === cur ? 26 : 9, height: 9, borderRadius: 999, background: k === cur ? C.orange : "rgba(255,255,255,0.5)", cursor: "pointer", transition: "all .3s", boxShadow: k === cur ? "0 0 12px rgba(255,102,0,0.7)" : "none" }} />)}
          </div>
        )}
      </React.Fragment>
    );
  }

  function BrandPanel() {
    const ts = { textShadow: "0 2px 20px rgba(0,0,0,0.55)" };
    return (
      <div className="portal-brand" style={{ position: "relative", overflow: "hidden", minHeight: "100vh", background: C.feedBg, padding: 48, display: "flex", flexDirection: "column", justifyContent: "space-between", color: "#fff" }}>
        <EntryCarousel />
        <img src="assets/logo-olab.png" alt="o-lab" style={{ height: 36, alignSelf: "flex-start", filter: "brightness(1.05) drop-shadow(0 2px 10px rgba(0,0,0,0.5))", position: "relative", zIndex: 2 }} />
        <div style={{ position: "relative", zIndex: 2 }}>
          <div style={{ display: "inline-flex", alignItems: "center", gap: 7, background: "rgba(198,242,78,0.18)", border: "1px solid rgba(198,242,78,0.45)", padding: "6px 12px", borderRadius: 999, marginBottom: 16, backdropFilter: "blur(4px)" }}>
            <span style={{ width: 8, height: 8, borderRadius: 999, background: C.lime, boxShadow: "0 0 10px rgba(198,242,78,0.9)", animation: "olabPulse 1.6s ease-in-out infinite" }} />
            <span style={{ fontSize: 11, fontWeight: 900, color: C.lime, letterSpacing: "0.06em" }}>TALENTO EN ACCIÓN</span>
          </div>
          <div style={{ fontSize: 44, lineHeight: 1.08, fontWeight: 900, letterSpacing: "-0.02em", ...ts }}>Que nuestro origen no limite<br /><span style={{ color: C.orange }}>nuestro futuro.</span></div>
          <div style={{ fontSize: 16.5, lineHeight: 1.6, color: "rgba(255,255,255,0.88)", marginTop: 16, maxWidth: 460, fontWeight: 600, ...ts }}>Demuestra tus competencias más allá de una hoja de vida.</div>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginTop: 22 }}>
            {[["XP & niveles", C.lime, "rgba(198,242,78,0.14)", "rgba(198,242,78,0.35)"], ["Streaks", "#FF9A4D", "rgba(255,102,0,0.18)", "rgba(255,102,0,0.4)"], ["Ranking", "#9E86FF", "rgba(108,76,241,0.2)", "rgba(108,76,241,0.42)"], ["Insignias", "#5EE7FF", "rgba(18,194,233,0.18)", "rgba(18,194,233,0.4)"]].map(([t, col, bg, bd]) => (
              <span key={t} style={{ fontSize: 11.5, fontWeight: 800, color: col, background: bg, border: `1px solid ${bd}`, padding: "7px 12px", borderRadius: 999, backdropFilter: "blur(4px)" }}>{t}</span>
            ))}
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 14, background: "rgba(255,255,255,0.10)", border: "1px solid rgba(255,255,255,0.18)", borderRadius: 18, padding: 14, marginTop: 24, maxWidth: 560, backdropFilter: "blur(6px)" }}>
            <div style={{ background: "#fff", borderRadius: 14, padding: "10px 12px", flexShrink: 0, display: "flex", alignItems: "center", justifyContent: "center", boxShadow: "0 6px 18px rgba(0,0,0,0.25)" }}>
              <img src="assets/fundacion-origen.png" alt="Fundación El Origen" style={{ height: 60, width: "auto", display: "block" }} />
            </div>
            <div style={{ fontSize: 12.5, lineHeight: 1.55, color: "rgba(255,255,255,0.92)", fontWeight: 600, ...ts }}>
              Este portal es una iniciativa para cerrar las brechas entre <b style={{ color: "#fff" }}>educación y empleabilidad</b>, liderada por <b style={{ color: "#fff" }}>Fundación El Origen</b> junto a instituciones y empresas aliadas globales.
            </div>
          </div>
        </div>
        <div style={{ fontSize: 12, color: "rgba(255,255,255,0.55)", fontWeight: 600, position: "relative", zIndex: 2, ...ts }}>O-Lab · portal de talento · aislado de la información interna</div>
      </div>
    );
  }

  const INP = { fontFamily: FONT, fontSize: 14, fontWeight: 600, color: C.ink, background: C.sand, border: `1px solid ${C.line2}`, borderRadius: 13, padding: "13px 14px", width: "100%" };
  const CAP = { fontSize: 10, fontWeight: 800, letterSpacing: "0.08em", color: C.mut4, marginBottom: 6, display: "block" };

  function Login() {
    const [role, setRole] = useState("aplicante");
    const [mode, setMode] = useState("login"); // login | reset | request
    const [form, setForm] = useState({ email: "", password: "" });
    const [busy, setBusy] = useState(false);
    const [msg, setMsg] = useState(null);
    const [showTerms, setShowTerms] = useState(false);
    const mob = useIsMobile(860);
    const set = (k) => (e) => setForm({ ...form, [k]: e.target.value });
    const apl = role === "aplicante";
    const accent = apl ? C.orange : C.violet;

    if (mode === "request") return <RequestForm role={role} onBack={() => setMode("login")} />;

    async function submit(e) {
      e.preventDefault(); setMsg(null); setBusy(true);
      try {
        if (mode === "reset") { await B().resetPassword(form.email); setMsg({ kind: "ok", text: "Te enviamos un correo para restablecer tu contraseña." }); }
        else { await B().login({ email: form.email, password: form.password }); await B().ensureRole(role); }
      } catch (err) { setMsg({ kind: "err", text: friendlyErr(err) }); } finally { setBusy(false); }
    }

    return (
      <div style={{ minHeight: "100vh", fontFamily: FONT, display: "grid", gridTemplateColumns: mob ? "1fr" : "1fr min(460px, 40%)" }}>
        <BrandPanel />
        <div style={{ background: C.cream, display: "flex", alignItems: "center", justifyContent: "center", padding: "40px 32px" }}>
          <div style={{ width: "100%", maxWidth: 400 }}>
            <img src="assets/logo-olab.png" alt="o-lab" className="portal-logo-sm" style={{ height: 28, marginBottom: 20, display: "none" }} />
            <div style={{ fontSize: 24, fontWeight: 800, color: C.ink, letterSpacing: "-0.01em" }}>{mode === "reset" ? "Recupera tu acceso" : "Únete a O-Lab"}</div>
            <div style={{ fontSize: 13, fontWeight: 600, color: C.mut, marginTop: 4 }}>{mode === "reset" ? "Te enviaremos un enlace a tu correo." : "Demuestra tus competencias más allá de una hoja de vida."}</div>

            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginTop: 20 }}>
              {[["aplicante", "Soy aplicante", "user", C.orange], ["empresa", "Soy empresa", "briefcase", C.violet]].map(([k, label, icon, col]) => {
                const on = role === k;
                return (
                  <button key={k} onClick={() => setRole(k)} style={{ border: `1.5px solid ${on ? col : C.line}`, background: on ? (k === "aplicante" ? C.orangeSoft : C.violetSoft) : "#fff", borderRadius: 16, padding: "13px 12px", cursor: "pointer", fontFamily: FONT, textAlign: "left", display: "flex", flexDirection: "column", gap: 8 }}>
                    <div style={{ width: 34, height: 34, borderRadius: 10, background: on ? col : C.sand, display: "flex", alignItems: "center", justifyContent: "center" }}><Icon name={icon} size={18} color={on ? "#fff" : C.mut} /></div>
                    <span style={{ fontSize: 13, fontWeight: 800, color: on ? (k === "aplicante" ? C.orangeDk : C.violetDk) : C.mut3 }}>{label}</span>
                  </button>
                );
              })}
            </div>

            {mode === "reset" ? (
              <form onSubmit={submit} style={{ display: "flex", flexDirection: "column", gap: 13, marginTop: 18 }}>
                <div><label style={CAP}>CORREO</label><input required type="email" value={form.email} onChange={set("email")} placeholder="tu@correo.com" style={INP} /></div>
                {msg && <div style={{ fontSize: 12, fontWeight: 700, color: msg.kind === "ok" ? C.green : "#C0392B", background: msg.kind === "ok" ? C.greenBg : "#FDECEA", border: `1px solid ${msg.kind === "ok" ? C.greenBd : "#F5C6C0"}`, borderRadius: 12, padding: "10px 12px" }}>{msg.text}</div>}
                <button type="submit" disabled={busy} style={{ border: 0, background: accent, color: "#fff", fontFamily: FONT, fontSize: 14, fontWeight: 800, padding: 14, borderRadius: 14, cursor: busy ? "default" : "pointer", opacity: busy ? 0.7 : 1 }}>{busy ? "Un momento…" : "Enviar enlace"}</button>
                <a onClick={() => { setMode("login"); setMsg(null); }} style={{ color: accent, fontWeight: 800, cursor: "pointer", textAlign: "center", fontSize: 13 }}>← Volver</a>
              </form>
            ) : (
              <React.Fragment>
                {/* CTA principal: solicitar acceso (la mayoría empieza aquí) */}
                <button onClick={() => { setMode("request"); setMsg(null); }} style={{ width: "100%", border: 0, background: "linear-gradient(135deg,#FF6600,#FF3D7F)", color: "#fff", fontFamily: FONT, fontSize: 17, fontWeight: 800, padding: "17px 16px", borderRadius: 16, cursor: "pointer", marginTop: 20, boxShadow: "0 12px 26px rgba(255,102,0,0.34)", display: "flex", alignItems: "center", justifyContent: "center", gap: 9 }}>
                  Solicitar acceso <span style={{ fontSize: 20 }}>→</span>
                </button>
                <div style={{ fontSize: 12.5, fontWeight: 600, color: C.mut2, marginTop: 8, textAlign: "center", lineHeight: 1.4 }}>👋 ¿Primera vez? La mayoría empieza aquí — te toma ~2 minutos.</div>

                <div style={{ display: "flex", alignItems: "center", gap: 10, margin: "20px 0 14px" }}>
                  <div style={{ flex: 1, height: 1, background: C.line }} /><span style={{ fontSize: 11, fontWeight: 800, color: C.mut4, letterSpacing: "0.04em" }}>¿YA TIENES CUENTA?</span><div style={{ flex: 1, height: 1, background: C.line }} />
                </div>
                <form onSubmit={submit} style={{ display: "flex", flexDirection: "column", gap: 11 }}>
                  <div><label style={CAP}>CORREO</label><input required type="email" value={form.email} onChange={set("email")} placeholder={apl ? "mariana@correo.com" : "talento@empresa.com"} style={INP} /></div>
                  <div>
                    <label style={CAP}>CONTRASEÑA</label>
                    <input required type="password" value={form.password} onChange={set("password")} placeholder="••••••••" style={INP} />
                    <button type="button" onClick={() => { setMode("reset"); setMsg(null); }} style={{ border: 0, background: "transparent", color: accent, fontFamily: FONT, fontSize: 12, fontWeight: 700, cursor: "pointer", marginTop: 8, padding: 0 }}>¿Olvidaste tu contraseña?</button>
                  </div>
                  {msg && <div style={{ fontSize: 12, fontWeight: 700, color: "#C0392B", background: "#FDECEA", border: "1px solid #F5C6C0", borderRadius: 12, padding: "10px 12px" }}>{msg.text}</div>}
                  <button type="submit" disabled={busy} style={{ border: `1.5px solid ${C.line2}`, background: "#fff", color: C.ink, fontFamily: FONT, fontSize: 14, fontWeight: 800, padding: 13, borderRadius: 14, cursor: busy ? "default" : "pointer", opacity: busy ? 0.7 : 1 }}>{busy ? "Un momento…" : "Iniciar sesión"}</button>
                </form>
              </React.Fragment>
            )}
            <div style={{ marginTop: 18, fontSize: 11.5, fontWeight: 600, color: C.mut, textAlign: "center", lineHeight: 1.5 }}>
              Al iniciar sesión o solicitar acceso aceptas los <span onClick={() => setShowTerms(true)} style={{ color: accent, fontWeight: 800, cursor: "pointer", textDecoration: "underline" }}>términos y condiciones</span> y el tratamiento de datos.
            </div>
          </div>
        </div>
        {showTerms && <TermsModal onClose={() => setShowTerms(false)} />}
      </div>
    );
  }

  const COMO_OPTS = ["Instagram", "LinkedIn", "TikTok", "Un amigo / conocido", "Un evento o feria", "Una institución educativa", "Búsqueda en Google", "Otro"];

  const COUNTRY_CODES = [
    { code: "+57", flag: "🇨🇴" }, { code: "+1", flag: "🇺🇸" }, { code: "+52", flag: "🇲🇽" }, { code: "+51", flag: "🇵🇪" },
    { code: "+593", flag: "🇪🇨" }, { code: "+58", flag: "🇻🇪" }, { code: "+56", flag: "🇨🇱" }, { code: "+54", flag: "🇦🇷" },
    { code: "+591", flag: "🇧🇴" }, { code: "+507", flag: "🇵🇦" }, { code: "+506", flag: "🇨🇷" }, { code: "+502", flag: "🇬🇹" },
    { code: "+503", flag: "🇸🇻" }, { code: "+504", flag: "🇭🇳" }, { code: "+505", flag: "🇳🇮" }, { code: "+34", flag: "🇪🇸" },
  ];

  const DEPARTAMENTOS = ["Amazonas", "Antioquia", "Arauca", "Atlántico", "Bogotá D.C.", "Bolívar", "Boyacá", "Caldas", "Caquetá", "Casanare", "Cauca", "Cesar", "Chocó", "Córdoba", "Cundinamarca", "Guainía", "Guaviare", "Huila", "La Guajira", "Magdalena", "Meta", "Nariño", "Norte de Santander", "Putumayo", "Quindío", "Risaralda", "San Andrés y Providencia", "Santander", "Sucre", "Tolima", "Valle del Cauca", "Vaupés", "Vichada"];
  const INSTITUCIONES = ["Institución Universitaria de Barranquilla", "Universidad del Norte", "Corporación Tecnológica Indoamericana", "Universidad de la Costa Atlántica", "Centro INCA Barranquilla", "Elyon Yireth Barranquilla", "Universidad Autónoma del Caribe", "Universidad Santiago de Cali", "Politécnico PIO", "Fundación Universitaria María Cano", "Universidad Nacional Abierta y a Distancia - UNAD", "Fundación Universitaria Monserrate", "Universidad Iberoamericana", "Universidad América", "INESCO", "Instituto I3", "Universidad de La Guajira", "Servicio Nacional de Aprendizaje - SENA", "Universidad El Bosque", "Universidad ECCI", "Universidad Militar Nueva Granada", "Universidad Javeriana", "Universidad Piloto", "Universidad Católica", "Universidad Minuto de Dios", "Universidad San José", "Universidad La Gran Colombia", "Fundación Universitaria Compensar", "Otra"];
  const PERFILES_FORMACION = ["Análisis de datos", "Ciencia de datos / Data Science", "Desarrollo web", "Diseño UX/UI", "Testing y aseguramiento de calidad de software", "Ciberseguridad", "Soporte de tecnologías de la información (TI)", "Inteligencia artificial", "Diseño gráfico potenciado con IA", "Marketing potenciado con IA", "Mercadeo para roles tecnodigitales", "Soporte e infraestructura de Data Center"];

  const PROGRAMA_SECTIONS = [
    { title: "1. Información personal", fields: [
      { k: "tipoDoc", label: "Tipo de documento", type: "select", options: ["Cédula de ciudadanía", "Tarjeta de identidad", "Cédula de extranjería", "Permiso por Protección Temporal (PPT)", "Pasaporte", "Otro"] },
      { k: "numDoc", label: "Número de identificación", type: "text" },
      { k: "fechaNac", label: "Fecha de nacimiento", type: "date" },
      { k: "sexo", label: "Sexo registrado al nacer", type: "select", options: ["Femenino", "Masculino", "Intersexual", "Prefiero no responder"] },
      { k: "genero", label: "¿Con cuál género te identificas?", type: "select", options: ["Mujer", "Hombre", "No binario", "Otro", "Prefiero no responder"] },
      { k: "orientacion", label: "Orientación sexual", type: "select", options: ["Heterosexual", "Gay", "Lesbiana", "Bisexual", "Otra", "Prefiero no responder"], optional: true },
    ] },
    { title: "2. Lugar de residencia", fields: [
      { k: "pais", label: "País", type: "text" },
      { k: "departamento", label: "Departamento", type: "select", options: DEPARTAMENTOS },
      { k: "zona", label: "Tipo de zona", type: "select", options: ["Urbana", "Rural", "Centro poblado"] },
    ] },
    { title: "3. Composición del hogar", fields: [
      { k: "viveCon", label: "¿Con quién vives actualmente?", type: "multi", options: ["Madre y/o padre", "Solo madre", "Solo padre", "Abuelos", "Otros familiares", "Pareja e hijos(as)", "Solo pareja", "Hijos(as)", "Amigos", "Solo(a)"] },
      { k: "responsabilidadEconomica", label: "Responsabilidad económica en el hogar", type: "select", options: ["Soy el principal responsable económico", "Comparto la responsabilidad con otra(s) persona(s)", "No soy responsable económicamente"] },
      { k: "tiempoCuidado", label: "Tiempo diario al cuidado de otras personas", type: "select", options: ["No realizo o menos de 1 hora al día", "Entre 1 y 3 horas al día", "Entre 4 y 6 horas al día", "Más de 6 horas al día"] },
    ] },
    { title: "4. Pertenencia poblacional", fields: [
      { k: "grupos", label: "¿Te identificas con alguno de estos grupos?", type: "multi", options: ["Pueblo indígena", "Afrocolombiano(a), negro(a), raizal o palenquero(a)", "Pueblo Rrom o Gitano", "Campesino(a)", "Migrante", "Víctima del conflicto armado", "Ninguno", "Otro", "Prefiero no responder"] },
      { k: "situacionMigratoria", label: "¿Alguna de estas situaciones aplica a ti?", type: "select", options: ["Persona migrante", "Persona refugiada o solicitante de refugio", "Persona retornada", "Persona desplazada internamente", "Víctima del conflicto armado", "Ninguna de las anteriores", "Prefiero no responder"], optional: true },
      { k: "lengua", label: "¿Hablas alguna lengua diferente al español? ¿Cuál?", type: "text", optional: true },
      { k: "discapacidad", label: "¿Tienes alguna discapacidad o condición permanente?", type: "select", options: ["No", "Física", "Visual", "Auditiva", "Intelectual", "Psicosocial", "Sordoceguera", "Otra"] },
    ] },
    { title: "5. Educación", fields: [
      { k: "nivelEducativo", label: "Nivel educativo más alto completado", type: "select", options: ["Sin escolaridad", "Primaria incompleta", "Primaria completa", "Secundaria", "Bachillerato", "Técnico", "Tecnólogo", "Universitario / profesional", "Especialización", "Maestría", "Doctorado"] },
      { k: "estudiaActualmente", label: "¿Actualmente estudias?", type: "select", options: ["Sí", "No"] },
      { k: "carrera", label: "Carrera, programa o formación", type: "text", optional: true },
      { k: "perfilesInteres", label: "¿En cuáles perfiles te gustaría formarte?", type: "multi", options: PERFILES_FORMACION.concat("Otro"), otherKey: "perfilesInteresOtro", otherOpt: "Otro" },
      { k: "nivelIngles", label: "Nivel de inglés", type: "select", options: ["Nulo (A0)", "Básico (A1-A2)", "Intermedio (B1-B2)", "Avanzado (C1-C2)"] },
    ] },
    { title: "6. Situación laboral", fields: [
      { k: "situacionLaboral", label: "Situación laboral actual", type: "select", options: ["Empleado formalmente", "Empleado informalmente", "Trabajo independiente o por cuenta propia", "Tengo un emprendimiento", "Estoy buscando empleo", "No trabajo ni estoy buscando empleo"] },
      { k: "haTrabajado", label: "¿Has trabajado anteriormente?", type: "select", options: ["Sí", "No"] },
    ] },
    { title: "7. Ingresos", fields: [
      { k: "ingresoMensual", label: "Ingreso personal mensual aproximado", type: "select", options: ["No recibo ingresos actualmente", "Menos de 1 SMMLV", "Entre 1 y 2 SMMLV", "Entre 2 y 4 SMMLV", "Más de 4 SMMLV"] },
      { k: "fuenteIngresos", label: "Principal fuente de ingresos", type: "select", options: ["Ingreso laboral formal", "Ingreso laboral informal", "Apoyo de un familiar", "Pago de prácticas", "Remesas", "Subsidios", "Otros"] },
      { k: "sisben", label: "Grupo del Sisbén", type: "select", options: ["A1-A5: Pobreza extrema", "B1-B7: Pobreza moderada", "C1-C18: Vulnerable", "D1-D21: No pobre ni vulnerable", "No estoy registrado(a)"] },
      { k: "estrato", label: "Estrato socioeconómico", type: "select", options: ["Estrato 1", "Estrato 2", "Estrato 3", "Estrato 4", "Estrato 5", "Estrato 6", "Rural"] },
    ] },
    { title: "8. Tecnología", fields: [
      { k: "dispositivos", label: "¿Con qué dispositivos cuentas?", type: "multi", options: ["Computador portátil", "Computador de escritorio", "Tableta", "Celular inteligente / smartphone", "Ninguno de los anteriores"] },
      { k: "internet", label: "¿Tienes acceso a internet en casa?", type: "select", options: ["Sí", "No"] },
    ] },
  ];

  function ProgramaForm({ prog, setProg, consent, setConsent }) {
    const [showLegal, setShowLegal] = useState(false);
    const setV = (k) => (e) => setProg({ ...prog, [k]: e.target.value });
    const toggleMulti = (k, opt) => { const arr = Array.isArray(prog[k]) ? prog[k] : []; setProg({ ...prog, [k]: arr.includes(opt) ? arr.filter((x) => x !== opt) : arr.concat(opt) }); };
    function field(fl) {
      if (fl.type === "multi") {
        const arr = Array.isArray(prog[fl.k]) ? prog[fl.k] : [];
        return (
          <div key={fl.k} style={{ gridColumn: "1 / -1" }}>
            <label style={CAP}>{fl.label}</label>
            <div style={{ display: "flex", flexWrap: "wrap", gap: 7 }}>
              {fl.options.map((o) => {
                const on = arr.includes(o);
                return <button type="button" key={o} onClick={() => toggleMulti(fl.k, o)} style={{ border: `1px solid ${on ? C.orange : C.line2}`, background: on ? C.orangeSoft : "#fff", color: on ? C.orangeDk : C.mut3, fontFamily: FONT, fontSize: 12, fontWeight: 700, padding: "7px 11px", borderRadius: 999, cursor: "pointer" }}>{on ? "✓ " : ""}{o}</button>;
              })}
            </div>
            {fl.otherKey && arr.includes(fl.otherOpt || "Otro") && (
              <input value={prog[fl.otherKey] || ""} onChange={setV(fl.otherKey)} placeholder="Escribe tu tema o perfil de interés…" style={{ ...INP, marginTop: 9 }} />
            )}
          </div>
        );
      }
      if (fl.type === "select") {
        return <div key={fl.k} style={{ gridColumn: "1 / -1" }}><label style={CAP}>{fl.label}</label><select required={!fl.optional} value={prog[fl.k] || ""} onChange={setV(fl.k)} style={{ ...INP, cursor: "pointer" }}><option value="">Selecciona…</option>{fl.options.map((o) => <option key={o} value={o}>{o}</option>)}</select></div>;
      }
      return <div key={fl.k}><label style={CAP}>{fl.label}</label><input required={!fl.optional} type={fl.type === "number" ? "number" : fl.type === "date" ? "date" : "text"} value={prog[fl.k] || ""} onChange={setV(fl.k)} style={INP} /></div>;
    }
    return (
      <div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
        <div style={{ background: C.orangeSoft, border: `1px solid ${C.orangeSoftBd}`, borderRadius: 14, padding: "12px 14px" }}>
          <div style={{ fontSize: 13, fontWeight: 800, color: C.orangeDk }}>Programa "De Clases a Carreras" · Fundación El Origen</div>
          <div style={{ fontSize: 12, fontWeight: 600, color: C.mut2, marginTop: 4, lineHeight: 1.5 }}>Queremos conocerte para acompañarte en tu proceso. No hay respuestas buenas o malas — responde con tranquilidad. Toma ~5 minutos.</div>
        </div>
        {PROGRAMA_SECTIONS.map((sec) => (
          <div key={sec.title}>
            <div style={{ fontSize: 14, fontWeight: 800, color: C.ink, marginBottom: 10 }}>{sec.title}</div>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", gap: 12 }}>{sec.fields.map(field)}</div>
          </div>
        ))}
        <div style={{ background: C.sand, borderRadius: 14, padding: 14 }}>
          <div style={{ fontSize: 13, fontWeight: 800, color: C.ink }}>Autorización de tratamiento de datos</div>
          <div style={{ fontSize: 12, fontWeight: 600, color: C.mut2, marginTop: 6, lineHeight: 1.5 }}>Autorizo a la Fundación El Origen y a FUNDACIÓN CORONA (NIT. 860008052-1), operador del programa GOYN Bogotá, a tratar mis datos personales e imagen para los fines del programa, conforme a su política de tratamiento de datos.</div>
          <button type="button" onClick={() => setShowLegal(!showLegal)} style={{ border: 0, background: "transparent", color: C.orange, fontFamily: FONT, fontSize: 12, fontWeight: 800, cursor: "pointer", marginTop: 6, padding: 0 }}>{showLegal ? "Ocultar texto completo" : "Ver autorización completa"}</button>
          {showLegal && <div style={{ maxHeight: 180, overflowY: "auto", fontSize: 11, fontWeight: 500, color: C.mut2, lineHeight: 1.6, marginTop: 8, background: "#fff", borderRadius: 10, padding: 12, whiteSpace: "pre-wrap" }}>{"Autorizo a FUNDACIÓN CORONA (NIT. 860008052-1), operador del programa Global Opportunity Youth Network – GOYN Bogotá, y a los terceros contratados, para utilizar mi información personal para los fines señalados en su política de tratamiento de datos, incluida la verificación y validación mediante otras plataformas o fuentes. Cedo los derechos de mi imagen (foto/video) para su publicación en campañas institucionales o promocionales en radio, televisión, prensa, boletines, página web y redes sociales de la fundación y sus iniciativas, en Colombia y fuera de ella, por tiempo indefinido, sin recibir compensación económica. Conozco que puedo acceder a la política en http://fundacioncorona.org y ejercer mis derechos escribiendo a tratamientodatospersonales@fcorona.org. Manifiesto libre y voluntariamente mi aceptación para participar en la Fase II del Fondo Quiero Ser Digital, iniciativa de GOYN Bogotá gestionada por la Corporación Inversor, y declaro cumplir los requisitos establecidos."}</div>}
          <label style={{ display: "flex", alignItems: "flex-start", gap: 9, marginTop: 12, cursor: "pointer" }}>
            <input type="checkbox" checked={consent} onChange={(e) => setConsent(e.target.checked)} style={{ marginTop: 3, width: 18, height: 18, accentColor: C.orange }} />
            <span style={{ fontSize: 12, fontWeight: 700, color: C.ink }}>He leído y <b>autorizo</b> el tratamiento de mis datos personales e imagen conforme a lo anterior.</span>
          </label>
        </div>
      </div>
    );
  }

  function RequestForm({ role: initialRole, onBack }) {
    const [role, setRole] = useState(initialRole || "aplicante");
    const apl = role === "aplicante";
    const accent = apl ? C.orange : C.violet;
    const [f, setF] = useState({});
    const [busy, setBusy] = useState(false);
    const [msg, setMsg] = useState(null);
    const programa = apl; // el programa es obligatorio para aplicantes (salvo empresas aliadas)
    const [step, setStep] = useState("basic"); // basic | programa
    const [prog, setProg] = useState({});
    const [consent, setConsent] = useState(false);
    const [empConsent, setEmpConsent] = useState(false);
    const [aplConsent, setAplConsent] = useState(false);
    const set = (k) => (e) => setF({ ...f, [k]: e.target.value });
    async function submit(e) {
      e.preventDefault();
      if (apl && step === "basic" && !aplConsent) { setMsg({ kind: "err", text: "Debes aceptar los términos y condiciones y el tratamiento de datos para continuar." }); return; }
      if (apl && programa && step === "basic") { setStep("programa"); try { window.scrollTo(0, 0); } catch (err) {} return; }
      if (apl && programa && step === "programa" && !consent) { setMsg({ kind: "err", text: "Debes autorizar el tratamiento de datos para postularte al programa." }); return; }
      if (!apl && !empConsent) { setMsg({ kind: "err", text: "Debes aceptar los términos y el tratamiento de datos para continuar." }); return; }
      setMsg(null); setBusy(true);
      try {
        const name = apl ? (f.name || "") : (f.contactName || "");
        const fullPhone = f.phone ? ((f.phoneCode || "+57") + " " + f.phone).trim() : "";
        const fields = apl
          ? { edad: f.edad || "", city: f.city || "", phone: fullPhone, situacion: f.situacion || "", institucion: f.institucion || "", comoNosEscucho: f.como || "", aceptaTerminos: true }
          : { razonSocial: f.razonSocial || "", nit: f.nit || "", sector: f.sector || "", contactName: f.contactName || "", cargo: f.cargo || "", empresa: f.razonSocial || "", phone: fullPhone, ciudad: f.ciudad || "", tamano: f.tamano || "", logo: f.logo || "", comoNosEscucho: f.como || "", linkedin: f.linkedin || "", instagram: f.instagram || "", facebook: f.facebook || "", aceptaTerminos: true, name: f.razonSocial || "" };
        if (apl && programa) {
          fields.programaClasesACarreras = true;
          // Fusiona lo ya respondido en el paso básico para no repetir preguntas.
          fields.programa = { ...prog, nombreCompleto: name, edadProg: f.edad || "", municipio: f.city || "", institucionProg: f.institucion || "", situacionBasica: f.situacion || "", telefono: fullPhone, correo: f.email || "", consent: true };
        }
        await B().requestAccount({ role, email: f.email, password: f.password, name, fields });
        // el gate de App muestra la pantalla de "solicitud enviada"
      } catch (err) { setMsg({ kind: "err", text: friendlyErr(err) }); setBusy(false); }
    }
    // OJO: son FUNCIONES que se llaman en línea ({field({...})}), NO componentes
    // (<Field/>). Definir un componente dentro del render lo remonta en cada tecla
    // y el input pierde el foco. Llamarlas evita ese remonte.
    const field = (o) => {
      if (o.type === "phone") {
        const code = f[o.k + "Code"] || "+57";
        return (
          <div key={o.k} style={{ gridColumn: o.half ? "auto" : "1 / -1" }}>
            <label style={CAP}>{o.label}</label>
            <div style={{ display: "flex", gap: 6 }}>
              <select value={code} onChange={set(o.k + "Code")} style={{ ...INP, width: 98, flex: "0 0 auto", cursor: "pointer", padding: "13px 6px" }}>
                {COUNTRY_CODES.map((c) => <option key={c.code} value={c.code}>{c.flag} {c.code}</option>)}
              </select>
              <input required={o.required !== false} type="tel" value={f[o.k] || ""} onChange={set(o.k)} placeholder="300 000 0000" autoComplete="off" style={{ ...INP, flex: 1, minWidth: 0 }} />
            </div>
          </div>
        );
      }
      return (
        <div key={o.k} style={{ gridColumn: o.half ? "auto" : "1 / -1" }}>
          <label style={CAP}>{o.label}</label>
          <input required={o.required !== false} type={o.type || "text"} value={f[o.k] || ""} onChange={set(o.k)} placeholder={o.placeholder || ""} autoComplete={o.type === "password" ? "new-password" : "off"} style={INP} />
        </div>
      );
    };
    const select = (o) => (
      <div key={o.k} style={{ gridColumn: o.half ? "auto" : "1 / -1" }}>
        <label style={CAP}>{o.label}</label>
        <select required={o.required !== false} value={f[o.k] || ""} onChange={set(o.k)} style={{ ...INP, cursor: "pointer" }}>
          <option value="">Selecciona…</option>
          {o.options.map((op) => <option key={op} value={op}>{op}</option>)}
        </select>
      </div>
    );
    return (
      <div style={{ minHeight: "100vh", fontFamily: FONT, background: C.feedBg, display: "flex", alignItems: "flex-start", justifyContent: "center", padding: "40px 20px" }}>
        <div style={{ width: "100%", maxWidth: step === "programa" ? 680 : 520, background: C.cream, borderRadius: 26, padding: "clamp(20px, 4vw, 30px)", animation: "olabRise .25s ease both" }}>
          <img src="assets/logo-olab.png" alt="o-lab" style={{ height: 28 }} />
          <div style={{ fontSize: 24, fontWeight: 800, color: C.ink, marginTop: 14 }}>Solicita tu acceso</div>
          <div style={{ fontSize: 13, fontWeight: 600, color: C.mut, marginTop: 4 }}>Cuéntanos de ti. El equipo O-Lab revisará tu solicitud y te dará acceso.</div>

          {step === "basic" && (
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginTop: 18 }}>
              {[["aplicante", "Soy aplicante", "user", C.orange], ["empresa", "Soy empresa", "briefcase", C.violet]].map(([k, label, icon, col]) => {
                const on = role === k;
                return <button key={k} onClick={() => setRole(k)} style={{ border: `1.5px solid ${on ? col : C.line}`, background: on ? (k === "aplicante" ? C.orangeSoft : C.violetSoft) : "#fff", borderRadius: 14, padding: "11px 12px", cursor: "pointer", fontFamily: FONT, display: "flex", alignItems: "center", gap: 8 }}><Icon name={icon} size={16} color={on ? col : C.mut} /><span style={{ fontSize: 13, fontWeight: 800, color: on ? (k === "aplicante" ? C.orangeDk : C.violetDk) : C.mut3 }}>{label}</span></button>;
              })}
            </div>
          )}

          <form onSubmit={submit} style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: 12, marginTop: 18 }}>
            {step === "programa" ? (
              <ProgramaForm prog={prog} setProg={setProg} consent={consent} setConsent={setConsent} />
            ) : apl ? (
              <React.Fragment>
                {field({ label: "NOMBRE Y APELLIDO", k: "name", placeholder: "Mariana Ríos" })}
                {field({ label: "EDAD", k: "edad", type: "number", placeholder: "24", half: true })}
                {field({ label: "CIUDAD DONDE VIVES", k: "city", placeholder: "Medellín", half: true })}
                {field({ label: "CELULAR / WHATSAPP", k: "phone", type: "phone", half: true })}
                {field({ label: "CORREO", k: "email", type: "email", placeholder: "mariana@correo.com", half: true })}
                {select({ label: "¿CUÁL ES TU SITUACIÓN?", k: "situacion", options: ["Estudiando", "Recién graduado", "Graduado hace más de 5 años", "Trabajando actualmente y buscando nuevas oportunidades", "Desempleado"] })}
                {field({ label: "¿A QUÉ INSTITUCIÓN PERTENECES / PERTENECISTE?", k: "institucion", placeholder: "Universidad / SENA / colegio…", required: false })}
                {select({ label: "¿DE DÓNDE ESCUCHASTE DE NOSOTROS?", k: "como", options: COMO_OPTS })}
                {field({ label: "CONTRASEÑA (para tu cuenta)", k: "password", type: "password", placeholder: "mínimo 6 caracteres" })}
                <div style={{ gridColumn: "1 / -1", background: "#fff", border: `1px solid ${C.line2}`, borderRadius: 14, padding: 14 }}>
                  <div style={{ fontSize: 11, fontWeight: 700, color: C.mut2, lineHeight: 1.55, maxHeight: 120, overflowY: "auto", background: C.sand, borderRadius: 10, padding: 11, marginBottom: 10 }}>
                    Al registrarme autorizo a O-Lab / Fundación El Origen a tratar mis datos personales aquí suministrados con el fin de gestionar mi acceso al portal de talento, mi participación en labs, tests y procesos de empleabilidad, y para conectarme con oportunidades laborales y empresas aliadas. Autorizo que mi perfil (foto, competencias y hoja de vida) sea visible para empresas dentro del portal. Los datos se conservan mientras mi cuenta esté activa y puedo actualizarlos o solicitar su eliminación escribiendo a O-Lab. Declaro que la información es veraz y que soy mayor de edad o cuento con autorización de mi acudiente.
                  </div>
                  <label style={{ display: "flex", alignItems: "flex-start", gap: 9, cursor: "pointer" }}>
                    <input type="checkbox" checked={aplConsent} onChange={(e) => setAplConsent(e.target.checked)} style={{ width: 18, height: 18, marginTop: 1, cursor: "pointer", flexShrink: 0 }} />
                    <span style={{ fontSize: 13, fontWeight: 700, color: C.ink }}>Acepto los términos y condiciones y el tratamiento de datos.</span>
                  </label>
                </div>
                <div style={{ gridColumn: "1 / -1", background: C.orangeSoft, border: `1.5px solid ${C.orange}`, borderRadius: 14, padding: 14, display: "flex", gap: 10, alignItems: "flex-start" }}>
                  <span style={{ fontSize: 22, lineHeight: 1 }}>🎓</span>
                  <div>
                    <div style={{ fontSize: 13, fontWeight: 800, color: C.orangeDk }}>Programa "De Clases a Carreras" · Fundación El Origen</div>
                    <div style={{ fontSize: 12, fontWeight: 600, color: C.mut2, marginTop: 3, lineHeight: 1.5 }}><b>Para ser considerado/a debes completar el formulario del programa</b> (~5 min). Salvo que vengas de parte de una empresa aliada, solo evaluamos el acceso de quienes hacen parte del programa. Presiona el botón de abajo para continuar 👇</div>
                  </div>
                </div>
              </React.Fragment>
            ) : (
              <React.Fragment>
                {field({ label: "RAZÓN SOCIAL", k: "razonSocial", placeholder: "Andina Logistics S.A.S.", half: true })}
                {field({ label: "NIT", k: "nit", placeholder: "900.123.456-7", half: true })}
                {field({ label: "SECTOR / INDUSTRIA", k: "sector", placeholder: "Logística, manufactura, retail…", half: true })}
                {select({ label: "TAMAÑO DE LA EMPRESA", k: "tamano", options: ["1-10", "11-50", "51-200", "+200"], half: true })}
                {field({ label: "NOMBRE DE CONTACTO (RRHH / RECLUTADOR)", k: "contactName", placeholder: "Tu nombre", half: true })}
                {field({ label: "CARGO DEL CONTACTO", k: "cargo", placeholder: "Gerente de talento", half: true })}
                {field({ label: "CORREO ELECTRÓNICO CORPORATIVO", k: "email", type: "email", placeholder: "talento@empresa.com", half: true })}
                {field({ label: "TELÉFONO / CELULAR", k: "phone", type: "phone", half: true })}
                {field({ label: "CIUDAD", k: "ciudad", placeholder: "Medellín", half: true })}
                {field({ label: "LOGO DE LA EMPRESA (URL, opcional)", k: "logo", placeholder: "https://…/logo.png", required: false, half: true })}
                {field({ label: "LINKEDIN (opcional)", k: "linkedin", placeholder: "https://linkedin.com/company/…", required: false, half: true })}
                {field({ label: "INSTAGRAM (opcional)", k: "instagram", placeholder: "https://instagram.com/…", required: false, half: true })}
                {field({ label: "FACEBOOK (opcional)", k: "facebook", placeholder: "https://facebook.com/…", required: false, half: true })}
                {select({ label: "¿CÓMO SE ENTERÓ DE NOSOTROS? (opcional)", k: "como", options: COMO_OPTS, required: false, half: true })}
                {field({ label: "CONTRASEÑA (para crear la cuenta)", k: "password", type: "password", placeholder: "mínimo 6 caracteres", half: true })}
                <div style={{ gridColumn: "1 / -1", background: "#fff", border: `1px solid ${C.line2}`, borderRadius: 14, padding: 14 }}>
                  <div style={{ fontSize: 11, fontWeight: 700, color: C.mut2, lineHeight: 1.55, maxHeight: 120, overflowY: "auto", background: C.sand, borderRadius: 10, padding: 11, marginBottom: 10 }}>
                    Al registrarme autorizo a O-Lab / Fundación El Origen a tratar los datos de la empresa y del contacto aquí suministrados con el fin de gestionar el acceso al portal de talento, publicar vacantes, contactar candidatos y enviar comunicaciones relacionadas. Los datos se conservarán mientras la cuenta esté activa y podrán actualizarse o eliminarse solicitándolo a O-Lab. Declaro que la información es veraz y que estoy autorizado/a para representar a la empresa.
                  </div>
                  <label style={{ display: "flex", alignItems: "flex-start", gap: 9, cursor: "pointer" }}>
                    <input type="checkbox" checked={empConsent} onChange={(e) => setEmpConsent(e.target.checked)} style={{ width: 18, height: 18, marginTop: 1, cursor: "pointer", flexShrink: 0 }} />
                    <span style={{ fontSize: 13, fontWeight: 700, color: C.ink }}>Acepto los términos y condiciones y el tratamiento de datos.</span>
                  </label>
                </div>
              </React.Fragment>
            )}
            {msg && <div style={{ gridColumn: "1 / -1", fontSize: 12, fontWeight: 700, color: "#C0392B", background: "#FDECEA", border: "1px solid #F5C6C0", borderRadius: 12, padding: "10px 12px" }}>{msg.text}</div>}
            <div style={{ gridColumn: "1 / -1", display: "flex", gap: 8, marginTop: 4 }}>
              <button type="button" onClick={() => step === "programa" ? setStep("basic") : onBack()} style={{ border: `1px solid ${C.line2}`, background: "#fff", color: C.mut3, fontFamily: FONT, fontSize: 14, fontWeight: 800, padding: 14, borderRadius: 14, cursor: "pointer" }}>{step === "programa" ? "← Atrás" : "Volver"}</button>
              <button type="submit" disabled={busy} style={{ flex: 1, border: 0, background: accent, color: "#fff", fontFamily: FONT, fontSize: 14, fontWeight: 800, padding: 14, borderRadius: 14, cursor: busy ? "default" : "pointer", opacity: busy ? 0.7 : 1 }}>{busy ? "Enviando…" : (step === "basic" && apl && programa ? "Continuar al formulario del programa →" : "Enviar solicitud")}</button>
            </div>
          </form>
        </div>
      </div>
    );
  }

  function PendingScreen({ access, onLogout }) {
    const rejected = access && access.status === "rejected";
    return (
      <div style={{ minHeight: "100vh", fontFamily: FONT, background: C.feedBg, display: "flex", alignItems: "center", justifyContent: "center", padding: 24 }}>
        <div style={{ width: "100%", maxWidth: 460, background: C.cream, borderRadius: 26, padding: 32, textAlign: "center" }}>
          <img src="assets/logo-olab.png" alt="o-lab" style={{ height: 30, marginBottom: 18 }} />
          <div style={{ width: 64, height: 64, borderRadius: 999, margin: "0 auto", background: rejected ? "#FDECEA" : C.orangeSoft, display: "flex", alignItems: "center", justifyContent: "center" }}>
            <Icon name={rejected ? "close" : "clock"} size={30} color={rejected ? "#C0392B" : C.orange} />
          </div>
          <div style={{ fontSize: 22, fontWeight: 800, color: C.ink, marginTop: 16 }}>{rejected ? "Solicitud no aprobada" : "Solicitud enviada ✅"}</div>
          <div style={{ fontSize: 14, fontWeight: 600, color: C.mut2, marginTop: 8, lineHeight: 1.5 }}>
            {rejected ? "Tu solicitud no fue aprobada por ahora. Si crees que es un error, escríbenos." : "El equipo O-Lab está revisando tu solicitud. Te daremos acceso muy pronto — vuelve a iniciar sesión más tarde."}
          </div>
          {!rejected && (
            <div style={{ background: C.orangeSoft, border: `1px solid ${C.orangeSoftBd}`, borderRadius: 14, padding: "12px 14px", marginTop: 16, fontSize: 12.5, fontWeight: 600, color: C.orangeDk, lineHeight: 1.5, textAlign: "left" }}>
              📌 <b>Importante:</b> salvo que vengas de parte de una <b>empresa aliada</b>, solo evaluamos el acceso si haces parte del programa <b>"De Clases a Carreras"</b>. Si aún no lo hiciste, vuelve a solicitar acceso y completa ese formulario para ser considerado/a.
            </div>
          )}
          <button onClick={onLogout} style={{ border: 0, background: "transparent", color: C.mut, fontFamily: FONT, fontSize: 13, fontWeight: 700, cursor: "pointer", marginTop: 18 }}>Cerrar sesión</button>
        </div>
      </div>
    );
  }

  // Acceso vencido: el superadmin fijó una fecha límite que ya pasó.
  function ExpiredScreen({ access, onLogout }) {
    const when = (() => { try { return new Date(access.accessExpiry + "T00:00:00").toLocaleDateString("es-CO", { day: "2-digit", month: "long", year: "numeric" }); } catch (e) { return access.accessExpiry; } })();
    return (
      <div style={{ minHeight: "100vh", fontFamily: FONT, background: C.feedBg, display: "flex", alignItems: "center", justifyContent: "center", padding: 24 }}>
        <div style={{ width: "100%", maxWidth: 460, background: C.cream, borderRadius: 26, padding: 32, textAlign: "center" }}>
          <img src="assets/logo-olab.png" alt="o-lab" style={{ height: 30, marginBottom: 18 }} />
          <div style={{ width: 64, height: 64, borderRadius: 999, margin: "0 auto", background: "#FDECEA", display: "flex", alignItems: "center", justifyContent: "center" }}>
            <Icon name="clock" size={30} color="#C0392B" />
          </div>
          <div style={{ fontSize: 22, fontWeight: 800, color: C.ink, marginTop: 16 }}>Tu acceso ha vencido</div>
          <div style={{ fontSize: 14, fontWeight: 600, color: C.mut2, marginTop: 8, lineHeight: 1.5 }}>
            Tu acceso al portal estuvo habilitado hasta el <b>{when}</b>. Si necesitas más tiempo, escríbele al equipo O-Lab para renovarlo.
          </div>
          <button onClick={onLogout} style={{ border: 0, background: "transparent", color: C.mut, fontFamily: FONT, fontSize: 13, fontWeight: 700, cursor: "pointer", marginTop: 18 }}>Cerrar sesión</button>
        </div>
      </div>
    );
  }

  // Se muestra cuando un usuario autenticado (p. ej. de Projects, con su mismo
  // correo y contraseña) entra sin un perfil de portal: elige con qué rol entrar.
  function RoleChoose({ user, onPick, onLogout, busy }) {
    const name = user.displayName || (user.email ? user.email.split("@")[0] : "");
    return (
      <div style={{ minHeight: "100vh", background: C.feedBg, display: "flex", alignItems: "center", justifyContent: "center", padding: 24, fontFamily: FONT }}>
        <div style={{ width: "100%", maxWidth: 460, background: C.cream, borderRadius: 26, padding: 32, textAlign: "center" }}>
          <img src="assets/logo-olab.png" alt="o-lab" style={{ height: 30, marginBottom: 18 }} />
          <div style={{ fontSize: 22, fontWeight: 800, color: C.ink, letterSpacing: "-0.01em" }}>Hola{name ? ", " + name : ""} 👋</div>
          <div style={{ fontSize: 14, fontWeight: 600, color: C.mut, marginTop: 6 }}>¿Cómo quieres entrar al portal de talento?</div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginTop: 22 }}>
            {[["aplicante", "Entrar como aplicante", "Busca vacantes, Labs y suma XP", "user", C.orange, C.orangeSoft, C.orangeDk], ["empresa", "Entrar como empresa", "Publica vacantes y ve candidatos", "briefcase", C.violet, C.violetSoft, C.violetDk]].map(([role, title, sub, icon, col, soft, dk]) => (
              <button key={role} disabled={busy} onClick={() => onPick(role)} style={{ border: `1.5px solid ${col}`, background: soft, borderRadius: 18, padding: "18px 14px", cursor: busy ? "default" : "pointer", fontFamily: FONT, textAlign: "left", display: "flex", flexDirection: "column", gap: 10, opacity: busy ? 0.6 : 1 }}>
                <div style={{ width: 40, height: 40, borderRadius: 12, background: col, display: "flex", alignItems: "center", justifyContent: "center" }}><Icon name={icon} size={20} color="#fff" /></div>
                <div>
                  <div style={{ fontSize: 15, fontWeight: 800, color: dk }}>{title}</div>
                  <div style={{ fontSize: 12, fontWeight: 600, color: C.mut2, marginTop: 3 }}>{sub}</div>
                </div>
              </button>
            ))}
          </div>
          <div style={{ fontSize: 12, fontWeight: 600, color: C.mut, marginTop: 16 }}>Puedes cambiar de rol cuando quieras desde el menú.</div>
          <button onClick={onLogout} style={{ border: 0, background: "transparent", color: C.mut, fontFamily: FONT, fontSize: 13, fontWeight: 700, cursor: "pointer", marginTop: 14 }}>Cerrar sesión</button>
        </div>
      </div>
    );
  }

  function App() {
    const [st, setSt] = useState(() => (window.PORTAL_BACKEND ? window.PORTAL_BACKEND.state : { ready: false }));
    const [busyRole, setBusyRole] = useState(false);
    const [roleOverride, setRoleOverride] = useState(null); // manda la elección del usuario

    useEffect(() => {
      if (!window.PORTAL_BACKEND) return;
      return window.PORTAL_BACKEND.onAuth((s) => setSt((prev) => {
        // si el usuario cerró sesión, limpia el override
        if (!s.user) setRoleOverride(null);
        return Object.assign({}, s);
      }));
    }, []);

    // El rol elegido por el usuario SIEMPRE manda sobre lo que resuelva el backend.
    async function pickRole(role) { setRoleOverride(role); setBusyRole(true); try { await B().ensureRole(role); } catch (e) { console.warn(e); } finally { setBusyRole(false); } }

    if (!st.ready) return <Splash />;
    if (!st.user) return <Login />;
    const onLogout = () => { setRoleOverride(null); B().logout(); };
    // Gate de aprobación: si el usuario tiene una solicitud pendiente/rechazada y
    // NO es admin/master, no entra hasta que el superadmin lo apruebe.
    if (st.access && st.access.status && st.access.status !== "approved" && !st.isAdmin) {
      return <PendingScreen access={st.access} onLogout={onLogout} />;
    }
    // Acceso con tiempo límite: si el superadmin fijó una fecha y ya pasó, se bloquea.
    if (st.access && st.access.accessExpiry && !st.isAdmin) {
      const exp = new Date(st.access.accessExpiry + "T23:59:59");
      if (!isNaN(exp.getTime()) && exp < new Date()) {
        return <ExpiredScreen access={st.access} onLogout={onLogout} />;
      }
    }
    const effectiveRole = roleOverride || st.role;
    if (!effectiveRole) return <RoleChoose user={st.user} onPick={pickRole} onLogout={onLogout} busy={busyRole} />;
    return effectiveRole === "empresa"
      ? <window.EmpresaApp profile={st.profile} onLogout={onLogout} onSwitchRole={() => pickRole("aplicante")} isAdmin={st.isAdmin} />
      : <window.AplicanteApp profile={st.profile} onLogout={onLogout} onSwitchRole={() => pickRole("empresa")} isAdmin={st.isAdmin} />;
  }

  function mount() {
    const el = document.getElementById("root");
    ReactDOM.createRoot(el).render(<App />);
  }
  if (window.PORTAL_BACKEND) mount();
  else window.addEventListener("portal-backend-ready", mount, { once: true });
  // respaldo por si el módulo tarda (no debería): monta igual tras un tick
  setTimeout(() => { if (!document.getElementById("root").hasChildNodes()) mount(); }, 1500);

  window.PortalApp = App;
})();
