// =====================================================
// Odinote — la corona de patrocinador
//
// Dibujada a mano y no sacada de la tipografía de iconos a propósito: el
// archivo de iconos que viaja con la app es un recorte, y si "crown" no
// estuviera dentro, en la esquina del botón aparecería la palabra "crown"
// escrita. Un SVG siempre se ve, pesa cuatro líneas y además deja elegir el
// dorado exacto, que es justo lo que aquí importa.
//
// Hay dos formas de sacarla:
//
//   · `<window.Corona />` — el dibujo suelto, para meterlo donde sea.
//   · `<window.CoronaBoton />` — el botón dorado de la barra, que es lo que
//     ve quien ha invitado a un café.
//
// Quién la lleva se decide en `patrocinio.js`.
// =====================================================

window.Corona = function Corona({ size = 16, className = '', title, insignia = false, apagada = false }) {
  // Como insignia va pegada a la esquina de otro botón (posición absoluta);
  // suelta se comporta como un elemento normal y la coloca quien la use.
  const clases = ['corona-dibujo', insignia ? 'corona-patrocinador' : '', className]
    .filter(Boolean).join(' ');
  const texto = title || window.t('Patrocinador de Oddinote', 'Oddinote supporter');
  // Dos degradados con nombres distintos: si compartieran id, el primero que
  // se pintara mandaria sobre el otro y la corona apagada saldria dorada.
  const idTinta = apagada ? 'odi-corona-gris' : 'odi-corona-oro';
  const tintas = apagada
    ? { alto: '#B9B4B0', medio: '#8E8A8E', bajo: '#6E6A6E', borde: '#4A474A', piedra: '#E6E3E0' }
    : { alto: '#F6D65C', medio: '#E0A82E', bajo: '#C1841B', borde: '#7A5310', piedra: '#FFF3C4' };

  return (
    <span className={clases} title={texto} aria-label={texto} style={{ width: size, height: size }}>
      <svg viewBox="0 0 24 24" width={size} height={size} fill="none" aria-hidden="true">
        <defs>
          <linearGradient id={idTinta} x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor={tintas.alto} />
            <stop offset="55%" stopColor={tintas.medio} />
            <stop offset="100%" stopColor={tintas.bajo} />
          </linearGradient>
        </defs>
        {/* Cuerpo: los tres picos y la base, de una sola pieza para que el
            borde la recorra entera y no se vean costuras entre partes. */}
        <path
          d="M3 8.5 L7 12.5 L12 4.5 L17 12.5 L21 8.5 L19.4 18.5 L4.6 18.5 Z"
          fill={'url(#' + idTinta + ')'}
          stroke={tintas.borde}
          strokeWidth="1.4"
          strokeLinejoin="round"
        />
        {/* Las tres piedras. Puntos, no círculos con borde: a este tamaño un
            borde las convierte en manchas. */}
        <circle cx="7" cy="12.2" r="1.15" fill={tintas.piedra} />
        <circle cx="12" cy="9.3" r="1.15" fill={tintas.piedra} />
        <circle cx="17" cy="12.2" r="1.15" fill={tintas.piedra} />
      </svg>
    </span>
  );
};

// El botón que se ve en la barra. Solo lo pinta quien tiene la corona, así que
// no hay que enseñárselo a nadie más ni explicar nada: quien lo ve, lo ha
// pagado.
//
// Que sea un botón y no una insignia pegada al perfil es deliberado: una marca
// de tres píxeles en una esquina no la ve nadie, y lo que se compra aquí es
// precisamente que se vea. El brillo respira despacio en vez de parpadear —
// un parpadeo en una barra de herramientas cansa a los diez minutos.
//
// Abre la ventana en los dos estados, y esto es lo importante: con la corona
// encendida antes solo sacaba un aviso de agradecimiento y ahí se acababa. Un
// botón que da las gracias y no hace nada más está roto — se pulsa una vez, se
// lee el mensaje y no se vuelve a tocar, así que quien había pagado no llegaba
// nunca al sitio donde se cambia el cursor, que es justo lo que había pagado.
// Las gracias siguen estando, dentro de la ventana, que es donde no estorban.
window.CoronaBoton = function CoronaBoton({ activo = false, onAbrir, size = 19 }) {
  const cosmeticos = window.t(
    'Tus cosméticos de patrocinador: elige tu cursor',
    'Your supporter cosmetics: choose your cursor'
  );
  const invita = window.t(
    'Apoya Oddinote y consigue la corona y el cursor dorado',
    'Support Oddinote and get the crown and the golden cursor'
  );
  const texto = activo ? cosmeticos : invita;

  return (
    <button
      className={'icon-btn lift corona-btn' + (activo ? '' : ' apagada')}
      title={texto}
      aria-label={texto}
      onClick={() => {
        window.playAudioTone && window.playAudioTone('click');
        // Encendida lleva al taller —el cursor— y apagada a lo que se consigue
        // y al panel para reclamar una donación ya hecha. Es la misma ventana:
        // ella sabe cuál de las dos caras toca enseñar.
        onAbrir && onAbrir();
      }}
    >
      <window.Corona size={size} title={texto} apagada={!activo} />
    </button>
  );
};

// ── "Doné y no veo mi corona" ──
//
// En Ko-fi se paga con el correo de PayPal o de la tarjeta, y muy a menudo no
// es el de Google: un Hotmail de toda la vida, un Yahoo del instituto. Esa
// persona ha pagado, no ve nada, y el programa no puede adivinarlo solo.
//
// Aquí dice con qué correo pagó y el servidor mira si esa donación existe y
// está sin reclamar. Va plegado porque a quien no le hace falta no debe
// estorbarle, y quien lo necesita lo busca.
//
// ── Dos cosas que antes estaban mal ──
//
// No se enseña sin sesión de Google. El servidor exige un identificador
// firmado por Google, así que sin sesión este panel solo servía para llevar a
// alguien a un formulario que iba a rechazarle sin que hubiera hecho nada mal
// —y encima con un mensaje sobre "verificar tu identidad" que no explicaba
// qué hacer—. Si no hay sesión, aquí no hay nada: primero se entra.
//
// Y no se pide el importe. Se pedía como prueba de que la donación era tuya,
// pero a los tres días nadie recuerda si fueron 3 o 5, el recibo se perdió, y
// Ko-fi cobra en la moneda de quien paga. Esa prueba dejaba fuera sobre todo a
// los donantes de verdad. Ahora basta el correo, y se comprueba solo en cuanto
// está escrito: un botón más era un paso más para llegar al mismo sitio.
window.PanelReclamo = function PanelReclamo({ onConcedida }) {
  const { useState, useEffect, useRef } = React;
  const P = window.Patrocinio;

  // ¿Hay sesión de Google? Se pregunta al montar y se sigue escuchando: quien
  // entra con su cuenta teniendo esta ventana abierta ve aparecer el panel sin
  // tener que cerrarla y volver.
  const [haySesion, setHaySesion] = useState(() => !!(P && P.haySesion && P.haySesion()));
  useEffect(() => {
    if (!P || !P.vigilaSesion) return;
    return P.vigilaSesion(setHaySesion);
  }, []);

  const [abierto, setAbierto] = useState(false);
  const [correo, setCorreo] = useState('');
  const [esperando, setEsperando] = useState(false);
  const [tardando, setTardando] = useState(false);
  const [aviso, setAviso] = useState(null);

  // Lo que ya se preguntó, para no repetir la misma pregunta en cada tecla que
  // se toca después. Y una petición en vuelo a la vez: como se dispara sola,
  // sin esto un correo escrito despacio saldría tres veces.
  const contestados = useRef({});
  const enCurso = useRef(false);
  const reloj = useRef(null);
  const relojLento = useRef(null);

  // Un mensaje por cada motivo. El del servidor no distingue entre "no existe"
  // y "ya es de otro" en todos los casos a propósito —decirlo sería una forma
  // de averiguar quién ha donado—, así que aquí se explica lo que la persona
  // puede hacer.
  const MENSAJES = {
    'ya-reclamado': window.t(
      'Esa donación ya está vinculada a otra cuenta.',
      'That donation is already linked to another account.'),
    'es-el-mismo': window.t(
      'Ese es el correo con el que ya has entrado. Si hubiera una donación a su nombre, ya tendrías la corona.',
      'That is the email you signed in with. If there were a donation under it, you would already have the crown.'),
    // Este mensaje salía antes como 'hay que iniciar sesión' a alguien que
    // estaba viendo 'Conectado mediante Google' dos centímetros más arriba, lo
    // cual es de locos. No es que no haya entrado: es que la sesión ante
    // Firebase, que es otra cosa, se ha perdido. Ahora se dice lo que pasa y
    // qué hacer.
    'sin-sesion': window.t(
      'Oddinote no consigue identificarte ante Google en este momento. Cierra sesión aquí abajo, vuelve a entrar y prueba otra vez.',
      'Oddinote cannot verify your Google identity right now. Sign out below, sign back in and try again.'),
    'correo-invalido': window.t('Ese correo no parece un correo.', 'That does not look like an email.'),
    // Este no lo vería nunca quien usa el programa: sale solo si la función
    // del servidor es anterior a la versión que dejó de pedir el importe. Se
    // dice lo que pasa en vez de un 'algo falló' que no lleva a ninguna parte.
    'importe-invalido': window.t(
      'El servidor todavía pide el importe. Falta desplegar la versión nueva de la función.',
      'The server still asks for the amount. The new function version has not been deployed yet.'),
    'no-cuadra': window.t(
      'No encontramos ninguna donación con ese correo. Míralo otra vez. Si acabas de pagar, espera un minuto.',
      'We could not find any donation with that email. Check it again. If you just paid, wait a minute.'),
    'demasiadas': window.t(
      'Demasiados intentos seguidos. Espera un minuto.',
      'Too many attempts in a row. Wait a minute.'),
    'sin-conexion': window.t('Sin conexión.', 'No connection.'),
    'error': window.t('Algo falló. Inténtalo más tarde.', 'Something went wrong. Try again later.'),
  };

  // Deliberadamente flojo: aquí no se valida el correo de nadie, solo se decide
  // cuándo merece la pena preguntar al servidor. Quien escribe "ana@" todavía
  // no ha terminado.
  const pareceCorreo = (c) => /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(String(c || '').trim());

  const comprueba = (valor, insistiendo) => {
    const c = String(valor || '').trim().toLowerCase();
    if (!P || !pareceCorreo(c) || enCurso.current) return;
    if (!insistiendo && contestados.current[c]) return;

    contestados.current[c] = true;
    enCurso.current = true;
    setEsperando(true);
    setAviso(null);
    setTardando(false);

    // El aviso de que está tardando. La espera puede irse a veinte segundos
    // cuando la función del servidor lleva rato apagada y tiene que encenderse,
    // y una pantalla quieta durante veinte segundos parece una pantalla rota.
    clearTimeout(relojLento.current);
    relojLento.current = setTimeout(() => setTardando(true), 6000);

    P.reclama(c).then((r) => {
      clearTimeout(relojLento.current);
      enCurso.current = false;
      setEsperando(false);
      setTardando(false);
      if (r.ok) {
        setAviso({ bien: true, texto: window.t('¡Listo! Tu corona ya está activa.', 'Done! Your crown is active.') });
        onConcedida && onConcedida();
        return;
      }
      setAviso({
        bien: false,
        texto: MENSAJES[r.motivo] || MENSAJES.error,
        // Volver a preguntar solo tiene sentido cuando la respuesta puede
        // cambiar: la donación que aún no ha llegado, la red que falló. Que
        // una donación sea de otro no va a cambiar por insistir.
        seArregla: r.motivo === 'no-cuadra' || r.motivo === 'sin-conexion' ||
                   r.motivo === 'demasiadas' || r.motivo === 'error',
      });
    });
  };

  // Se comprueba solo, poco después de dejar de escribir. Novecientos
  // milisegundos: menos y salta a mitad de un correo escrito despacio, más y
  // parece que no hace nada.
  useEffect(() => {
    clearTimeout(reloj.current);
    const c = correo.trim().toLowerCase();
    if (!abierto || !pareceCorreo(c) || contestados.current[c]) return;
    reloj.current = setTimeout(() => comprueba(c), 900);
    return () => clearTimeout(reloj.current);
  }, [correo, abierto]);

  useEffect(() => () => { clearTimeout(reloj.current); clearTimeout(relojLento.current); }, []);

  // Sin sesión de Google no hay panel. Ver el comentario de arriba.
  if (!haySesion) return null;

  if (!abierto) {
    return (
      <button
        onClick={() => { setAbierto(true); window.playAudioTone && window.playAudioTone('click'); }}
        style={{
          display: 'flex', alignItems: 'center', gap: '7px',
          background: 'rgba(224, 168, 46, 0.10)', cursor: 'pointer',
          border: '1.5px solid rgba(224, 168, 46, 0.55)', borderRadius: '8px',
          padding: '9px 12px', fontSize: '12.5px', fontWeight: 600,
          color: 'var(--ink, #1A1A1A)', textAlign: 'left', width: '100%',
        }}
      >
        <window.Corona size={15} apagada title="" />
        {window.t('¿Donaste y no ves tu corona?', 'Donated and cannot see your crown?')}
      </button>
    );
  }

  const campo = {
    padding: '9px 10px', borderRadius: '6px', fontSize: '13px', width: '100%',
    border: '1.5px solid var(--line-soft, #E5E1DD)', background: 'var(--paper, #FFF)',
    color: 'var(--ink, #1A1A1A)',
  };

  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: '9px', padding: '12px',
      borderRadius: '8px', border: '1.5px solid var(--line-soft, #E5E1DD)',
      background: 'rgba(224, 168, 46, 0.07)',
    }}>
      <div style={{ fontSize: '12.5px', color: 'var(--ink-3, #595459)', lineHeight: 1.45 }}>
        {window.t(
          'Si pagaste con un correo distinto al de tu Google, escríbelo aquí. Se comprueba solo.',
          'If you paid with an email other than your Google one, type it here. It checks itself.')}
      </div>

      <input
        type="email"
        value={correo}
        autoFocus
        onChange={(e) => { setCorreo(e.target.value); setAviso(null); }}
        onKeyDown={(e) => { if (e.key === 'Enter') { clearTimeout(reloj.current); comprueba(correo, true); } }}
        placeholder={window.t('Correo con el que pagaste', 'Email you paid with')}
        style={campo}
      />

      {esperando && (
        <div style={{
          display: 'flex', alignItems: 'center', gap: '8px',
          fontSize: '12.5px', color: 'var(--ink-3, #595459)', lineHeight: 1.45,
        }}>
          <span className="material-symbols-rounded corona-girando" style={{ fontSize: 16 }}>progress_activity</span>
          <span>
            {tardando
              ? window.t('Está tardando más de lo normal. Sigue en marcha.', 'This is taking longer than usual. Still going.')
              : window.t('Comprobando…', 'Checking…')}
          </span>
        </div>
      )}

      {!esperando && aviso && (
        <div style={{
          fontSize: '12.5px', lineHeight: 1.45, padding: '8px 10px', borderRadius: '6px',
          color: 'var(--ink, #1A1A1A)',
          background: aviso.bien ? 'rgba(144, 185, 104, 0.16)' : 'rgba(230, 84, 79, 0.10)',
          border: '1.5px solid ' + (aviso.bien ? 'var(--olive-texto)' : 'var(--wine, #E6544F)'),
        }}>
          {aviso.texto}
          {aviso.seArregla && (
            <button
              onClick={() => comprueba(correo, true)}
              style={{
                display: 'block', marginTop: '6px', padding: 0, border: 'none', background: 'none',
                color: 'var(--olive-texto)', fontWeight: 700, fontSize: '12.5px', cursor: 'pointer',
              }}
            >
              {window.t('Probar otra vez', 'Try again')}
            </button>
          )}
        </div>
      )}
    </div>
  );
};

// ── La ventana de la corona ──
//
// Tiene la suya y no comparte la del perfil, que es donde estaba antes. No es
// una manía de orden: en la del perfil, la corona era un párrafo más entre el
// correo y el botón de cerrar sesión, y lo que se compra con una donación es
// precisamente que se note.
//
// Quien no tiene corona ve lo que se consigue y por dónde. Quien la tiene ve el
// taller: aquí se cambia el cursor, que es lo que de verdad hace que el programa
// se parezca un poco a ti.
window.VentanaCorona = function VentanaCorona({ esPatrocinador, onCerrar, onConcedida }) {
  const { useState, useEffect, useRef } = React;
  const C = window.CursorOdinote;

  const [cfg, setCfg] = useState(() => (C && C.lee()) || { modo: 'color', color: '#E0A82E', imagen: null });
  const [aviso, setAviso] = useState(null);
  // Si hay algo guardado, "El de siempre" tiene trabajo; si no, no lo tiene y
  // se apaga. Un botón que no puede hacer nada y aun así se deja pulsar es la
  // forma más rápida de que alguien crea que la aplicación no le responde.
  const [hayGuardado, setHayGuardado] = useState(() => !!(C && C.lee()));
  // El acuse de recibo del botón de guardar, que dura lo que dura la lectura.
  const [recienGuardado, setRecienGuardado] = useState(false);

  // Se va aplicando mientras se toca, no al guardar. Un cursor no se puede
  // elegir a ciegas: hay que verlo moverse para saber si estorba.
  useEffect(() => {
    if (!C || !esPatrocinador) return;
    C.aplica(cfg);
  }, [cfg, esPatrocinador]);

  // Y al cerrar manda lo que hay guardado, no lo que se estuvo probando. Sin
  // esto, quien paseaba por la rueda de color y cerraba sin guardar se quedaba
  // con el último tono que rozó el ratón hasta reiniciar la aplicación, sin
  // haber dicho que sí en ningún momento.
  const esPatro = useRef(esPatrocinador);
  esPatro.current = esPatrocinador;
  useEffect(() => () => {
    if (!C) return;
    C.aplica(esPatro.current ? C.lee() : null);
  }, []);

  // El acuse va en el propio botón y no en un aviso debajo: la mano está en el
  // botón y la mirada también, así que es ahí donde hay que contestar.
  const guardar = () => {
    C && C.guarda(cfg);
    setHayGuardado(true);
    setRecienGuardado(true);
    window.playAudioTone && window.playAudioTone('click');
    setTimeout(() => setRecienGuardado(false), 1800);
  };

  const restablecer = () => {
    const limpio = { modo: 'color', color: '#E0A82E', imagen: null };
    setCfg(limpio);
    C && C.guarda(null);
    C && C.aplica(null);
    setHayGuardado(false);
    window.playAudioTone && window.playAudioTone('click');
    setAviso(window.t('Cursor de siempre restablecido.', 'Default cursor restored.'));
    setTimeout(() => setAviso(null), 2200);
  };

  const subirImagen = (e) => {
    const archivo = e.target.files && e.target.files[0];
    e.target.value = '';
    if (!archivo || !C) return;
    C.preparaImagen(archivo)
      .then(img => setCfg({ ...cfg, modo: 'imagen', imagen: img }))
      .catch(() => setAviso(window.t(
        'Ese archivo no se pudo usar. Prueba con un PNG o un JPG.',
        'That file could not be used. Try a PNG or a JPG.')));
  };

  return (
    <div
      className="modal-backdrop"
      onClick={(e) => { if (e.target === e.currentTarget) onCerrar(); }}
      style={{
        position: 'fixed', inset: 0, zIndex: 9000, display: 'grid', placeItems: 'center',
        background: 'rgba(0,0,0,0.45)', padding: '20px',
      }}
    >
      <div className="corona-taller">
        <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
          <window.Corona size={34} apagada={!esPatrocinador} title="" />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontWeight: 700, fontSize: '16px', lineHeight: 1.2 }}>
              {esPatrocinador
                ? window.t('Tus cosméticos', 'Your cosmetics')
                : window.t('Apoya Oddinote', 'Support Oddinote')}
            </div>
            <div style={{ fontSize: '12px', color: 'var(--ink-3, #595459)', marginTop: '2px' }}>
              {esPatrocinador
                ? window.t('Gracias por invitar a un café.', 'Thanks for the coffee.')
                : window.t('Un café, y el programa se parece un poco más a ti.', 'One coffee, and the app looks a little more like you.')}
            </div>
          </div>
          <button
            className="icon-btn lift"
            onClick={onCerrar}
            title={window.t('Cerrar', 'Close')}
            style={{ width: '32px', height: '32px', flexShrink: 0 }}
          >
            <span className="material-symbols-rounded" style={{ fontSize: 18 }}>close</span>
          </button>
        </div>

        <hr className="corona-hilo" />

        {esPatrocinador ? (
          <>
            <div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
              <div className="corona-rotulo">
                {window.t('Tu cursor', 'Your cursor')}
              </div>

              <div style={{ display: 'flex', gap: '8px' }}>
                <button
                  className={'corona-pestana' + (cfg.modo === 'color' ? ' activa' : '')}
                  onClick={() => setCfg({ ...cfg, modo: 'color' })}
                >
                  {window.t('Un color', 'A colour')}
                </button>
                <button
                  className={'corona-pestana' + (cfg.modo === 'imagen' ? ' activa' : '')}
                  onClick={() => setCfg({ ...cfg, modo: 'imagen' })}
                >
                  {window.t('Una imagen', 'An image')}
                </button>
              </div>

              {cfg.modo === 'color' ? (
                <window.SelectorColor
                  valor={cfg.color || '#E0A82E'}
                  colores={window.COLORES_ODINOTE_CURSOR}
                  compacto
                  acento="linear-gradient(180deg, #F6D65C 0%, #E0A82E 55%, #C1841B 100%)"
                  acentoTexto="#3E2C05"
                  onCambio={(c) => setCfg({ ...cfg, color: c })}
                />
              ) : (
                <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
                  <label className="corona-imagen">
                    <span className="material-symbols-rounded" style={{ fontSize: 18 }}>add_photo_alternate</span>
                    {cfg.imagen ? window.t('Cambiar la imagen', 'Change the image') : window.t('Elegir una imagen', 'Choose an image')}
                    <input type="file" accept="image/*" onChange={subirImagen} style={{ display: 'none' }} />
                  </label>
                  <div style={{ fontSize: '11px', color: 'var(--ink-3, #595459)', lineHeight: 1.4 }}>
                    {window.t(
                      'Se recorta a 40 píxeles. Los navegadores ignoran los cursores más grandes, así que no se puede hacer otra cosa.',
                      'It is cropped to 40 pixels. Browsers ignore larger cursors, so there is no way around it.')}
                  </div>
                </div>
              )}

              {/* Aquí había una zona para "probar" el cursor. No servía de nada:
                  el cursor elegido se aplica a la ventana entera en cuanto se
                  toca, así que ya se está probando en todas partes. Un recuadro
                  que pide hacer lo que ya estás haciendo solo ocupa sitio. */}
            </div>

            {aviso && (
              <div style={{
                fontSize: '12.5px', padding: '8px 10px', borderRadius: '7px',
                background: 'rgba(144, 185, 104, 0.16)',
                border: '1.5px solid var(--olive-texto)',
              }}>{aviso}</div>
            )}

            <div style={{ display: 'flex', gap: '8px' }}>
              <button
                className={'corona-guardar' + (recienGuardado ? ' hecho' : '')}
                onClick={guardar}
                style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '7px' }}
              >
                {recienGuardado && (
                  <span className="material-symbols-rounded" style={{ fontSize: 18 }}>check_circle</span>
                )}
                {recienGuardado ? window.t('Guardado', 'Saved') : window.t('Guardar', 'Save')}
              </button>
              <button
                className="corona-normal"
                onClick={restablecer}
                disabled={!hayGuardado}
                title={hayGuardado
                  ? window.t('Volver al cursor de siempre', 'Back to the usual cursor')
                  : window.t('Ya llevas el cursor de siempre', 'You already have the usual cursor')}
              >
                <span className="material-symbols-rounded" style={{ fontSize: 17 }}>undo</span>
                {window.t('El de siempre', 'Default one')}
              </button>
            </div>
          </>
        ) : (
          <>
            <div style={{
              display: 'flex', flexDirection: 'column', gap: '9px',
              padding: '14px', borderRadius: '10px',
              background: 'rgba(224, 168, 46, 0.08)',
              border: '1.5px solid rgba(224, 168, 46, 0.45)',
            }}>
              {[
                window.t('La corona dorada, en tu barra.', 'The gold crown, in your toolbar.'),
                window.t('El cursor en el color que elijas.', 'The cursor in the colour you choose.'),
                window.t('O con una imagen tuya.', 'Or with an image of your own.'),
              ].map((t, i) => (
                <div key={i} style={{ display: 'flex', gap: '9px', alignItems: 'flex-start', fontSize: '13px' }}>
                  <span className="material-symbols-rounded" style={{ fontSize: 17, color: '#C9962A' }}>check</span>
                  <span>{t}</span>
                </div>
              ))}
              <div style={{ fontSize: '11.5px', color: 'var(--ink-3, #595459)', lineHeight: 1.45, marginTop: '2px' }}>
                {window.t(
                  'Nada de Oddinote está detrás de esto: la aplicación entera es gratis y lo seguirá siendo. Esto son adornos.',
                  'None of Oddinote is behind this: the whole app is free and will stay that way. These are ornaments.')}
              </div>
            </div>

            <button
              className="btn lift"
              onClick={() => window.open('https://ko-fi.com/neuroxcx', '_blank', 'noopener,noreferrer')}
              style={{
                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '8px',
                padding: '11px', borderRadius: '8px', border: 'none', cursor: 'pointer',
                background: 'var(--wine, #7B2D26)', color: '#FFF', fontWeight: 700, fontSize: '13px',
              }}
            >
              <span className="material-symbols-rounded" style={{ fontSize: 18 }}>coffee</span>
              {window.t('Invitar a un café', 'Buy a coffee')}
            </button>

            <window.PanelReclamo onConcedida={onConcedida} />
          </>
        )}
      </div>
    </div>
  );
};
