/* global React */
// Shared primitives — buttons w/ unified hover (mint) + press (forest) states,
// and a slide-up bottom-sheet modal.

const { useState: useStateS, useEffect: useEffectS } = React;

// ────────────────────────────────────────────────
// Color tokens for the unified button states
//   default → ink (#333)
//   hover   → mint-700
//   pressed → forest-700 (slightly deeper, shows the press)
// ────────────────────────────────────────────────
const BTN_DEFAULT = "var(--yo-ink)";
const BTN_HOVER   = "var(--yo-mint-700)";
const BTN_PRESS   = "var(--yo-forest-700)";

window.YO_BTN = { BTN_DEFAULT, BTN_HOVER, BTN_PRESS };

// ────────────────────────────────────────────────
// PrimaryButton — Yomee Design System / CTA
//
// Spec (DO NOT diverge):
//   • Height: 56px · Border radius: 16px (rounded-square — NOT pill)
//   • Font: var(--font-mono), 700, 13px, letter-spacing 0.14em, UPPERCASE (CSS-enforced)
//   • Press effect: scale(0.985) over 100ms
//   • Width: 100% of parent (callers control max-width via wrapper)
//
// Variants:
//   variant="primary" (default)
//     bg: var(--yo-ink) → mint-700 hover → forest-700 press
//     fg: #fff
//   variant="accent" (requires accentColor prop)
//     bg: accentColor → brightness 0.93 hover → 0.88 press
//     fg: #fff
//     Use case: profile-colored CTAs (e.g. UploadScreen per-profile primary)
//   variant="outlined"
//     bg: transparent → ink/4% hover → ink/10% press
//     fg: var(--yo-ink) · border: 1.5px solid var(--yo-ink)
//     Use case: secondary action paired with a primary CTA in the same stack
//
// Props:
//   children      — label (sentence case; CSS uppercases it)
//   onClick       — handler
//   leftIcon      — optional ReactNode rendered absolute-left
//   trailingArrow — when true, renders a → after the label
//   variant       — "primary" | "accent" | "outlined"
//   accentColor   — hex/CSS color (required when variant === "accent")
//   style         — escape hatch for spacing only (do NOT override colors/font)
//   disabled      — dims and disables
//
// Usage:
//   <PrimaryButton onClick={...}>Continuar</PrimaryButton>
//   <PrimaryButton onClick={...} variant="accent" accentColor="#2BAE80" trailingArrow>
//     Crear mi radiografía
//   </PrimaryButton>
//   <PrimaryButton onClick={...} variant="outlined">Ver demo</PrimaryButton>
// ────────────────────────────────────────────────
function PrimaryButton({
  children,
  onClick,
  leftIcon,
  trailingArrow = false,
  variant = "primary",   // "primary" | "accent" | "outlined"
  accentColor,           // required only when variant === "accent" (e.g. "#2BAE80")
  style,
  disabled,
}) {
  const [hover, setHover]     = useStateS(false);
  const [pressed, setPressed] = useStateS(false);

  // Variant-aware visual tokens. PrimaryButton is the only CTA component in the
  // system; it adapts to three visual treatments without breaking the spec.
  let bg, fg, borderStyle;

  if (variant === "outlined") {
    // Transparent fill, ink border, ink text. Hover fills with light ink overlay,
    // press deepens it. No accent color allowed — outlined is always ink.
    fg = "var(--yo-ink)";
    borderStyle = "1.5px solid var(--yo-ink)";
    bg = disabled
      ? "transparent"
      : pressed
        ? "rgba(20,32,28,0.10)"
        : hover
          ? "rgba(20,32,28,0.04)"
          : "transparent";
  } else if (variant === "accent") {
    // Accent uses caller-provided color as default. Hover/press are simulated
    // via brightness filter to avoid defining 4×3 tokens (one per profile).
    if (!accentColor) {
      console.warn("PrimaryButton variant='accent' requires accentColor prop");
    }
    fg = "#fff";
    borderStyle = "none";
    bg = disabled ? "rgba(20,32,28,0.22)" : (accentColor || "var(--yo-ink)");
  } else {
    // "primary" — original ink behavior, unchanged.
    fg = "#fff";
    borderStyle = "none";
    bg = disabled
      ? "rgba(20,32,28,0.22)"
      : pressed ? BTN_PRESS : hover ? BTN_HOVER : BTN_DEFAULT;
  }

  // Brightness filter for accent hover/press (no-op for primary/outlined)
  const filter = (variant === "accent" && !disabled)
    ? (pressed ? "brightness(0.88)" : hover ? "brightness(0.93)" : "none")
    : "none";

  return (
    <button
      onClick={disabled ? undefined : onClick}
      disabled={disabled}
      onMouseEnter={() => !disabled && setHover(true)}
      onMouseLeave={() => { setHover(false); setPressed(false); }}
      onMouseDown={() => !disabled && setPressed(true)}
      onMouseUp={() => setPressed(false)}
      style={{
        position: "relative",
        width: "100%",
        height: 56,
        borderRadius: 16,
        background: bg,
        color: fg,
        border: borderStyle,
        filter,
        padding: "0 22px",
        cursor: disabled ? "not-allowed" : "pointer",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        gap: 10,
        fontFamily: "var(--font-mono)",
        fontWeight: 700,
        fontSize: 13,
        letterSpacing: "0.14em",
        textTransform: "uppercase",
        transform: pressed ? "scale(0.985)" : "scale(1)",
        transition: "background 180ms ease-out, transform 100ms ease-out, filter 160ms ease-out",
        ...style,
      }}
    >
      {leftIcon && (
        <span
          style={{
            position: "absolute",
            left: 22,
            top: "50%",
            transform: "translateY(-50%)",
            display: "flex",
            alignItems: "center",
            justifyContent: "center",
          }}
        >
          {leftIcon}
        </span>
      )}
      <span style={{ display: "inline-flex", alignItems: "center", gap: 8, whiteSpace: "nowrap" }}>
        {children}
        {trailingArrow && (
          <span aria-hidden="true" style={{ fontSize: 16, lineHeight: 1, marginLeft: 2 }}>
            →
          </span>
        )}
      </span>
    </button>
  );
}

// ────────────────────────────────────────────────
// BottomSheetModal — slides up from the bottom inside the
// IOSDevice (uses position:absolute with respect to the device).
// ────────────────────────────────────────────────
function BottomSheetModal({ open, title, onClose, children }) {
  // Mount-then-animate: 'rendered' controls DOM presence, 'entered' controls
  // the open transform. We mount FIRST in the closed state, then flip 'entered'
  // on the next frame so the transition actually runs.
  const [rendered, setRendered] = useStateS(false);
  const [entered, setEntered]   = useStateS(false);

  useEffectS(() => {
    if (open) {
      setRendered(true);
      // Wait two frames so the closed transform is committed before flipping.
      const r1 = requestAnimationFrame(() => {
        const r2 = requestAnimationFrame(() => setEntered(true));
        // Stash inner id so we can cancel
        cleanup.r2 = r2;
      });
      const cleanup = { r1 };
      return () => {
        cancelAnimationFrame(cleanup.r1);
        if (cleanup.r2) cancelAnimationFrame(cleanup.r2);
      };
    } else {
      setEntered(false);
    }
  }, [open]);

  // Lock body scroll while open (no-op inside iframe but tidy)
  useEffectS(() => {
    if (open) document.body.style.overflow = "hidden";
    else document.body.style.overflow = "";
    return () => { document.body.style.overflow = ""; };
  }, [open]);

  if (!rendered) return null;

  return (
    <div
      onTransitionEnd={(e) => {
        // Only unmount on the SHEET's transition (not the scrim's)
        if (!open && e.propertyName === "transform") setRendered(false);
      }}
      style={{
        position: "absolute",
        inset: 0,
        zIndex: 100,
        pointerEvents: open ? "auto" : "none",
      }}
    >
      {/* Scrim */}
      <div
        onClick={onClose}
        style={{
          position: "absolute",
          inset: 0,
          background: "rgba(20, 20, 20, 0.45)",
          opacity: entered ? 1 : 0,
          transition: "opacity 280ms ease-out",
        }}
      />

      {/* Sheet */}
      <div
        style={{
          position: "absolute",
          left: 0,
          right: 0,
          bottom: 0,
          maxHeight: "85%",
          background: "#fff",
          borderTopLeftRadius: 24,
          borderTopRightRadius: 24,
          borderTop: "2px solid var(--yo-ink)",
          borderLeft: "2px solid var(--yo-ink)",
          borderRight: "2px solid var(--yo-ink)",
          transform: entered ? "translateY(0)" : "translateY(100%)",
          transition: "transform 360ms cubic-bezier(.2,.8,.2,1)",
          display: "flex",
          flexDirection: "column",
          overflow: "hidden",
          boxShadow: "0 -10px 40px rgba(0,0,0,0.18)",
        }}
      >
        {/* Drag handle */}
        <div style={{ display: "flex", justifyContent: "center", paddingTop: 10 }}>
          <div style={{ width: 44, height: 4, borderRadius: 2, background: "rgba(51,51,51,0.25)" }} />
        </div>

        {/* Header */}
        <div
          style={{
            display: "flex",
            alignItems: "center",
            justifyContent: "space-between",
            padding: "14px 20px 8px",
          }}
        >
          <h2
            style={{
              margin: 0,
              fontFamily: "var(--font-mono)",
              fontWeight: 700,
              fontSize: 16,
              letterSpacing: "0.01em",
              color: "var(--yo-ink)",
              flex: 1,
              minWidth: 0,
              paddingRight: 12,
              whiteSpace: "nowrap",
              overflow: "hidden",
              textOverflow: "ellipsis",
            }}
          >
            {title}
          </h2>
          <button
            onClick={onClose}
            aria-label="Cerrar"
            style={{
              width: 32,
              height: 32,
              borderRadius: "50%",
              background: "transparent",
              border: "1.5px solid var(--yo-ink)",
              padding: 0,
              cursor: "pointer",
              fontSize: 16,
              fontWeight: 500,
              color: "var(--yo-ink)",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              lineHeight: 1,
            }}
          >
            ×
          </button>
        </div>

        {/* Content */}
        <div
          style={{
            flex: 1,
            overflow: "auto",
            padding: "10px 24px 28px",
            fontFamily: "var(--font-body)",
            fontSize: 14,
            lineHeight: "22px",
            color: "var(--yo-ink)",
            letterSpacing: "0.01em",
          }}
        >
          {children}
        </div>
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────
// Fake Spanish content for the legal modals
// ────────────────────────────────────────────────
const TERMS_BODY = (
  <>
    <p style={{ margin: 0, fontFamily: "var(--font-mono)", fontSize: 12, letterSpacing: "0.1em", textTransform: "uppercase", opacity: 0.7 }}>
      Última actualización: 28 de abril de 2026
    </p>

    <h3 style={legalH3}>1. Aceptación del servicio</h3>
    <p style={legalP}>
      Al utilizar Yomee aceptas estos términos y condiciones en su totalidad. Si no estás de
      acuerdo con alguno de ellos, te pedimos abstenerte de usar la aplicación. Yomee es una
      herramienta de orientación financiera personal y no constituye asesoría profesional ni una
      oferta de productos crediticios.
    </p>

    <h3 style={legalH3}>2. Uso de la cuenta</h3>
    <p style={legalP}>
      Eres responsable de mantener la confidencialidad de tus credenciales y de toda actividad
      que se realice desde tu cuenta. Yomee podrá suspender o cancelar tu acceso si detecta
      uso indebido, fraudulento o contrario a la legislación aplicable en los Estados Unidos
      Mexicanos.
    </p>

    <h3 style={legalH3}>3. Diagnóstico financiero (Radiografía)</h3>
    <p style={legalP}>
      Los resultados del diagnóstico se generan a partir de la información que tú proporcionas
      y de cálculos públicos sobre el Costo Anual Total (CAT). Las recomendaciones tienen
      carácter informativo; te invitamos a contrastarlas con un asesor de confianza antes de
      tomar decisiones financieras importantes.
    </p>

    <h3 style={legalH3}>4. Recomendaciones de productos</h3>
    <p style={legalP}>
      Yomee podrá sugerir tarjetas, créditos o productos de instituciones financieras reguladas.
      Estas sugerencias se basan en tu perfil; no implican aprobación automática y los términos
      finales dependen de cada emisor. Yomee no cobra comisión por mostrarte estas
      recomendaciones.
    </p>

    <h3 style={legalH3}>5. Limitación de responsabilidad</h3>
    <p style={legalP}>
      Yomee se ofrece "tal cual" y hace su mejor esfuerzo por mantener la información actualizada.
      No nos hacemos responsables por pérdidas derivadas de decisiones tomadas con base en el
      diagnóstico, ni por interrupciones del servicio fuera de nuestro control.
    </p>

    <h3 style={legalH3}>6. Modificaciones</h3>
    <p style={legalP}>
      Podemos actualizar estos términos cuando sea necesario. Te notificaremos de cambios
      relevantes a través de la app y por correo electrónico. El uso continuado del servicio
      después de la notificación implica tu aceptación de la nueva versión.
    </p>

    <h3 style={legalH3}>7. Contacto</h3>
    <p style={legalP}>
      Si tienes preguntas sobre estos términos, escríbenos a{" "}
      <span style={{ fontWeight: 700 }}>hola@yomee.ai</span>. Responderemos en un plazo máximo de
      cinco días hábiles.
    </p>
  </>
);

const PRIVACY_BODY = (
  <>
    <p style={{ margin: 0, fontFamily: "var(--font-mono)", fontSize: 12, letterSpacing: "0.1em", textTransform: "uppercase", opacity: 0.7 }}>
      Vigencia: 28 de abril de 2026
    </p>

    <h3 style={legalH3}>Quién es el responsable</h3>
    <p style={legalP}>
      Yomee Tecnología, S.A.P.I. de C.V., con domicilio en la Ciudad de México, es la
      responsable del tratamiento de tus datos personales. Contamos con un encargado de
      protección de datos disponible en <span style={{ fontWeight: 700 }}>privacidad@yomee.ai</span>.
    </p>

    <h3 style={legalH3}>Qué información recabamos</h3>
    <p style={legalP}>
      Recabamos datos de identificación (nombre, correo), datos financieros que tú compartes
      (montos de deuda, pagos, tarjetas registradas) y datos técnicos (modelo de dispositivo,
      versión del sistema). Solo recolectamos lo estrictamente necesario para ofrecerte tu
      diagnóstico.
    </p>

    <h3 style={legalH3}>Para qué los usamos</h3>
    <p style={legalP}>
      Utilizamos tus datos para generar tu radiografía financiera, sugerirte productos con
      mejores condiciones y mejorar el servicio. Nunca venderemos tu información a terceros con
      fines publicitarios.
    </p>

    <h3 style={legalH3}>Con quién los compartimos</h3>
    <p style={legalP}>
      Compartimos información con proveedores tecnológicos (almacenamiento en la nube,
      analítica) bajo acuerdos estrictos de confidencialidad. En ningún caso entregamos datos
      sensibles a instituciones financieras sin tu consentimiento expreso.
    </p>

    <h3 style={legalH3}>Tus derechos ARCO</h3>
    <p style={legalP}>
      Puedes <span style={{ fontWeight: 700 }}>Acceder, Rectificar, Cancelar u Oponerte</span> al
      tratamiento de tus datos en cualquier momento, escribiendo a privacidad@yomee.ai. También
      puedes revocar tu consentimiento o limitar el uso y divulgación de tu información.
    </p>

    <h3 style={legalH3}>Seguridad</h3>
    <p style={legalP}>
      Aplicamos medidas físicas, técnicas y administrativas para proteger tus datos: cifrado en
      tránsito y en reposo, acceso por roles y monitoreo continuo. Si detectamos un incidente
      relevante, te notificaremos en un plazo no mayor a 72 horas.
    </p>

    <h3 style={legalH3}>Cambios al aviso</h3>
    <p style={legalP}>
      Si modificamos este aviso, publicaremos la nueva versión en la app y te enviaremos un
      resumen de los cambios por correo electrónico antes de su entrada en vigor.
    </p>
  </>
);

const legalH3 = {
  margin: "20px 0 6px",
  fontFamily: "var(--font-mono)",
  fontWeight: 700,
  fontSize: 14,
  letterSpacing: "0.02em",
  color: "var(--yo-ink)",
};
const legalP = {
  margin: 0,
  fontFamily: "var(--font-body)",
  fontSize: 14,
  lineHeight: "22px",
  letterSpacing: "0.01em",
  color: "var(--yo-ink)",
};

window.PrimaryButton = PrimaryButton;
window.BottomSheetModal = BottomSheetModal;
window.TERMS_BODY = TERMS_BODY;
window.PRIVACY_BODY = PRIVACY_BODY;

// ────────────────────────────────────────────────
// HelpTooltip — GLOBAL, top-right anchored.
// Single source of truth for the app. Mounted once at the root via
// <GlobalHelpHost />. Header "?" buttons open it by calling
// window.YomeeHelp.toggle(screenKey). Only one tooltip is open at a time.
// Content is defined statically per screenKey; if no content exists,
// the toggle is a no-op so the icon just doesn't do anything.
// ────────────────────────────────────────────────
const HELP_COPY = {
  signup: {
    title: "¿Por qué pedimos esto?",
    body:
      "Solo usamos tu correo y contraseña para guardar tus consultas y mantener tu cuenta segura. Nunca compartimos tus datos con terceros.",
  },
  signin: {
    title: "¿Necesitas ayuda?",
    body:
      "Si olvidaste tu contraseña, usa la opción de recuperación debajo del botón de acceso.",
  },
};

// ────────────────────────────────────────────────
// HELP_SHEET_COPY — bottom-sheet help content (separate from tooltip HELP_COPY).
// One entry per screenKey. window.YomeeHelp.toggle(screenKey) opens the sheet
// when the key has an entry here. Keys: "personal-info", "otp",
// "hagamos-numeros", "upload".
//
// Each entry: { title, paragraphs: [string, ...] }
// ────────────────────────────────────────────────
const HELP_SHEET_COPY = {
  "personal-info": {
    title: "¿Por qué te pedimos esto?",
    paragraphs: [
      "Tu nombre nos permite personalizar tu experiencia y dirigirnos a ti de forma cercana.",
      "Tu edad nos ayuda a entender tu momento financiero y ofrecerte contexto relevante para tu etapa de vida.",
      "Tu género nos permite comunicarnos contigo con el lenguaje correcto.",
      "Ninguno de estos datos se comparte con terceros ni se usa para fines publicitarios.",
    ],
  },
  "otp": {
    title: "¿Problemas con tu código?",
    paragraphs: [
      "Puedes solicitar un nuevo código cada 60 segundos usando la opción 'Reenviar código' en la pantalla anterior.",
      "Si el problema persiste o necesitas ayuda adicional, escríbenos a hola@yomee.ai y te respondemos a la brevedad.",
    ],
  },
  "hagamos-numeros": {
    title: "No necesitas los números exactos",
    paragraphs: [
      "Los montos que ingreses aquí son solo para darte una primera idea de tu situación financiera.",
      "Puedes usar cifras aproximadas — si debes alrededor de $60,000, escribe eso. El resultado seguirá siendo útil.",
      "Tu radiografía real y detallada se genera a partir de tu estado de cuenta, no de estos números.",
    ],
  },
  "profile-result": {
    title: "¿Cómo determinamos tu perfil?",
    paragraphs: [
      "Tu perfil se basa en la relación entre tu deuda total y tu pago mensual. Es una primera lectura, no un diagnóstico definitivo.",
      "Totalero: pagas todo cada mes. Optimizador: pagas más del mínimo. Escalador: pagas cerca del mínimo. Reiniciador: hoy no estás pagando.",
      "Tu radiografía completa puede ajustar este perfil cuando analicemos tu estado de cuenta real.",
    ],
  },
  "upload": {
    title: "Tu información está segura",
    paragraphs: [
      "Tu estado de cuenta se usa únicamente para generar tu radiografía financiera. Nadie más tiene acceso a él.",
      "Una vez procesado, el archivo se elimina de forma permanente de nuestros servidores — no lo almacenamos, no lo vendemos, no lo compartimos.",
      "Solo conservamos los datos estructurados necesarios para mostrarte tus insights: montos, fechas y categorías.",
    ],
  },
};

// External controller — header buttons call this directly.
// open(screenKey) opens that screen's tooltip; close() closes whatever's open;
// toggle(screenKey) opens it (or closes if it's already that key).
window.YomeeHelp = window.YomeeHelp || {
  _listeners: new Set(),
  _active: null,
  _subscribe(fn) { this._listeners.add(fn); return () => this._listeners.delete(fn); },
  _emit() { this._listeners.forEach((fn) => fn(this._active)); },
  _has(screenKey) { return !!(HELP_COPY[screenKey] || HELP_SHEET_COPY[screenKey]); },
  open(screenKey) {
    if (!this._has(screenKey)) return; // no-op if no content
    this._active = screenKey;
    this._emit();
  },
  close() { this._active = null; this._emit(); },
  toggle(screenKey) {
    if (!this._has(screenKey)) return; // no-op if no content
    this._active = this._active === screenKey ? null : screenKey;
    this._emit();
  },
};

function GlobalHelpHost() {
  const [active, setActive] = useStateS(window.YomeeHelp._active);
  const [entered, setEntered] = useStateS(false);
  const [rendered, setRendered] = useStateS(!!window.YomeeHelp._active);
  const [flipUp, setFlipUp] = useStateS(false);
  const cardRef = React.useRef(null);

  useEffectS(() => {
    return window.YomeeHelp._subscribe(setActive);
  }, []);

  // Mount/unmount with fade in (150ms ease-out) / fade out (100ms)
  useEffectS(() => {
    if (active) {
      setRendered(true);
      const r1 = requestAnimationFrame(() =>
        requestAnimationFrame(() => setEntered(true))
      );
      return () => cancelAnimationFrame(r1);
    } else {
      setEntered(false);
      const t = setTimeout(() => setRendered(false), 110);
      return () => clearTimeout(t);
    }
  }, [active]);

  // Flip up if the card would overflow vertically.
  useEffectS(() => {
    if (!rendered || !cardRef.current) return;
    const el = cardRef.current;
    // Measure against the device frame's content area, not the OS window.
    const host = el.offsetParent || document.body;
    const hostRect = host.getBoundingClientRect();
    const rect = el.getBoundingClientRect();
    const overflowsBottom = rect.bottom > hostRect.bottom - 8;
    setFlipUp(overflowsBottom);
  }, [rendered, active]);

  if (!rendered || !active) return null;
  const copy = HELP_COPY[active];
  if (!copy) return null;

  // Anchor: 8px gap below the "?" icon. The icon sits at top:70 (auth) or
  // top:64+4=68 (onboarding), with height 30. Use the larger so we never
  // overlap. 70 + 30 + 8 = 108.
  const TOP_OFFSET = 108;

  return (
    <>
      {/* Tap-outside scrim — covers everything below the tooltip */}
      <div
        onClick={() => window.YomeeHelp.close()}
        style={{
          position: "absolute",
          inset: 0,
          background: "transparent",
          zIndex: 9998,
          opacity: entered ? 1 : 0,
          transition: "opacity 100ms ease-out",
        }}
      />

      <div
        ref={cardRef}
        role="dialog"
        aria-label={copy.title}
        style={{
          position: "absolute",
          top: flipUp ? undefined : TOP_OFFSET,
          bottom: flipUp ? 32 : undefined,
          right: 16,
          width: 280,
          maxWidth: "calc(100% - 32px)",
          zIndex: 9999,
          background: "var(--yo-ink)",
          color: "var(--yo-bg)",
          borderRadius: 18,
          padding: "16px 16px 18px",
          boxShadow: "0 14px 32px -12px rgba(20,32,28,0.35)",
          fontFamily: "var(--font-body)",
          opacity: entered ? 1 : 0,
          transform: entered ? "translateY(0)" : "translateY(-4px)",
          transition: entered
            ? "opacity 150ms ease-out, transform 150ms ease-out"
            : "opacity 100ms ease-out, transform 100ms ease-out",
        }}
      >
        {/* Tail pointing up-right at the ? icon (suppressed when flipped) */}
        {!flipUp && (
          <div
            aria-hidden
            style={{
              position: "absolute",
              top: -7,
              right: 14,
              width: 14,
              height: 14,
              background: "var(--yo-ink)",
              transform: "rotate(45deg)",
              borderRadius: 3,
            }}
          />
        )}
        {flipUp && (
          <div
            aria-hidden
            style={{
              position: "absolute",
              bottom: -7,
              right: 14,
              width: 14,
              height: 14,
              background: "var(--yo-ink)",
              transform: "rotate(45deg)",
              borderRadius: 3,
            }}
          />
        )}

        <div
          style={{
            display: "flex",
            alignItems: "flex-start",
            justifyContent: "space-between",
            gap: 12,
            marginBottom: 6,
            position: "relative",
          }}
        >
          <div
            style={{
              fontFamily: "var(--font-mono)",
              fontWeight: 500,
              fontSize: 15,
              lineHeight: "20px",
              letterSpacing: "0.005em",
              color: "var(--yo-bg)",
            }}
          >
            {copy.title}
          </div>
          <button
            onClick={() => window.YomeeHelp.close()}
            aria-label="Cerrar"
            style={{
              flex: "0 0 auto",
              width: 22,
              height: 22,
              borderRadius: "50%",
              background: "rgba(255,255,255,0.12)",
              border: "none",
              color: "var(--yo-bg)",
              fontSize: 13,
              lineHeight: 1,
              cursor: "pointer",
              padding: 0,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
            }}
          >
            ✕
          </button>
        </div>

        <div
          style={{
            fontSize: 13,
            lineHeight: "19px",
            color: "rgba(247,243,233,0.82)",
          }}
        >
          {copy.body}
        </div>
      </div>
    </>
  );
}

window.GlobalHelpHost = GlobalHelpHost;

// ────────────────────────────────────────────────
// HelpSheetHost — bottom-sheet help modal.
// Drives off the same window.YomeeHelp controller as GlobalHelpHost, but
// only renders for screenKeys that have an entry in HELP_SHEET_COPY (the
// tooltip host renders the rest). Mounted once at the app root.
//
// Spec (per design):
//   • Slides up from bottom, ~200ms ease-out
//   • White background, 20px top border-radius
//   • Handle bar: 36×4, #E0E0E0, centered, 8px from top
//   • Backdrop: rgba(0,0,0,0.4); tap to close
//   • Title: 17px, Geist Mono, weight 900, #1a1a1a
//   • Body: 14px, #555, line-height 1.6, paragraphs separated by 12px gap
//   • Close CTA: "Entendido" — full-width, black bg, white, mono caps,
//     16px margin-top
// ────────────────────────────────────────────────
function HelpSheetHost() {
  const [active, setActive] = useStateS(window.YomeeHelp._active);
  const [rendered, setRendered] = useStateS(!!window.YomeeHelp._active);
  const [entered, setEntered] = useStateS(false);

  useEffectS(() => {
    return window.YomeeHelp._subscribe(setActive);
  }, []);

  // Only react to keys that belong to the sheet host.
  const copy = active ? HELP_SHEET_COPY[active] : null;

  useEffectS(() => {
    if (copy) {
      setRendered(true);
      const r1 = requestAnimationFrame(() => {
        const r2 = requestAnimationFrame(() => setEntered(true));
        cleanup.r2 = r2;
      });
      const cleanup = { r1 };
      return () => {
        cancelAnimationFrame(cleanup.r1);
        if (cleanup.r2) cancelAnimationFrame(cleanup.r2);
      };
    } else {
      setEntered(false);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [copy]);

  if (!rendered) return null;
  // copy may briefly be null while exit-animating — keep the last copy in a
  // ref-like fallback so the closing transition still has content to render.
  const displayCopy = copy || HelpSheetHost._last;
  if (copy) HelpSheetHost._last = copy;
  if (!displayCopy) return null;

  const close = () => window.YomeeHelp.close();

  return (
    <div
      onTransitionEnd={(e) => {
        if (!copy && e.propertyName === "transform") setRendered(false);
      }}
      style={{
        position: "absolute",
        inset: 0,
        zIndex: 9997,
        pointerEvents: copy ? "auto" : "none",
      }}
    >
      {/* Backdrop */}
      <div
        onClick={close}
        style={{
          position: "absolute",
          inset: 0,
          background: "rgba(0,0,0,0.4)",
          opacity: entered ? 1 : 0,
          transition: "opacity 200ms ease-out",
        }}
      />

      {/* Sheet */}
      <div
        role="dialog"
        aria-label={displayCopy.title}
        style={{
          position: "absolute",
          left: 0,
          right: 0,
          bottom: 0,
          maxHeight: "85%",
          background: "#ffffff",
          borderTopLeftRadius: 20,
          borderTopRightRadius: 20,
          transform: entered ? "translateY(0)" : "translateY(100%)",
          transition: "transform 200ms ease-out",
          display: "flex",
          flexDirection: "column",
          overflow: "hidden",
          boxShadow: "0 -10px 40px rgba(0,0,0,0.18)",
        }}
      >
        {/* Handle bar */}
        <div style={{ display: "flex", justifyContent: "center", paddingTop: 8 }}>
          <div
            aria-hidden
            style={{
              width: 36,
              height: 4,
              borderRadius: 2,
              background: "#E0E0E0",
            }}
          />
        </div>

        {/* Content */}
        <div
          style={{
            padding: "16px 24px 24px",
            overflow: "auto",
          }}
        >
          <h2
            style={{
              margin: "8px 0 14px",
              fontFamily:
                '"Geist Mono", ui-monospace, "SFMono-Regular", Menlo, monospace',
              fontWeight: 900,
              fontSize: 17,
              lineHeight: 1.3,
              color: "#1a1a1a",
            }}
          >
            {displayCopy.title}
          </h2>

          <div
            style={{
              display: "flex",
              flexDirection: "column",
              gap: 12,
            }}
          >
            {displayCopy.paragraphs.map((p, i) => (
              <p
                key={i}
                style={{
                  margin: 0,
                  fontFamily:
                    '"Onest", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
                  fontSize: 14,
                  lineHeight: 1.6,
                  color: "#555555",
                }}
              >
                {p}
              </p>
            ))}
          </div>

          <div style={{ marginTop: 16 }}>
            <PrimaryButton onClick={close}>Entendido</PrimaryButton>
          </div>
        </div>
      </div>
    </div>
  );
}

window.HelpSheetHost = HelpSheetHost;

// Legacy shim — older code paths reference HelpTooltip / window.HelpTooltip
// directly. Keep them as no-op components so nothing crashes; the real
// tooltip is now driven by the global controller above (window.YomeeHelp +
// <GlobalHelpHost />).
function HelpTooltip() { return null; }
window.HelpTooltip = HelpTooltip;
