// =====================================================
// Odinote — top toolbar v4 (tools draggable to canvas)
// =====================================================

// Cada nodo dice a qué grupo pertenece; el color de su icono lo pone la hoja
// de estilos (.tool-icon--<grupo>, variables --tool-<grupo>-bg del tema).
const TOOLS = [
  // Texto / contenido
  { id: 'note',     icon: 'sticky_note_2', label: 'tool_note',     grupo: 'escribir' },
  { id: 'todo',     icon: 'checklist',     label: 'tool_todo',     grupo: 'escribir' },
  { id: 'doc',      icon: 'description',   label: 'tool_doc',      grupo: 'escribir' },
  { id: 'bigtitle', icon: 'title',         label: 'tool_bigtitle', grupo: 'escribir' },
  // Medios / archivos
  { id: 'image',    icon: 'image',         label: 'tool_image',    grupo: 'medios' },
  { id: 'audio',    icon: 'audiotrack',    label: 'tool_audio',    grupo: 'medios' },
  { id: 'link',     icon: 'link',          label: 'tool_link',     grupo: 'medios' },
  { id: 'color',    icon: 'palette',       label: 'tool_color',    grupo: 'medios' },
  // Estructura
  { id: 'board',    icon: 'dashboard',     label: 'tool_board',    grupo: 'estructura' },
  { id: 'column',   icon: 'view_column',   label: 'tool_column',   grupo: 'estructura' },
  // La figura en el sitio de la tabla, que se usa poco (dicho por el usuario):
  // la tabla sigue en el menú de More.
  { id: 'shape',    icon: 'category',      label: 'tool_shape',    grupo: 'estructura' },
  { id: 'frame',    icon: 'crop_free',     label: 'tool_frame',    grupo: 'estructura' },
  // Conector y dibujo a mano
  { id: 'line',     icon: 'arrow_outward', label: 'tool_line',     grupo: 'trazos' },
  { id: 'draw',     icon: 'gesture',       label: 'tool_draw',     grupo: 'trazos' },
];

// touch.js marca <html data-mobile="1"> cuando la pantalla es de movil. Se
// consulta en el momento de dibujar: cambia al girar el aparato o al redimensionar.
function esMovil() {
  return typeof document !== 'undefined' &&
    document.documentElement.getAttribute('data-mobile') === '1';
}

// Las de más van en el desplegable y no en la barra: en una pantalla de 1080
// entran quince botones justos, y meter el dieciséis a la fuerza deja la barra
// con desplazamiento, que es peor que un clic de más.
// El mismo orden por grupos que la barra de arriba, que es de donde cuelga
// este menú: primero lo que se escribe, luego los medios y archivos y al final
// la estructura.
const EXTRA_TOOLS = [
  // Texto / contenido
  { id: 'comment',  icon: 'forum',         label: 'tool_comment',  grupo: 'escribir' },
  { id: 'code',     icon: 'code',          label: 'tool_code',     grupo: 'escribir' },
  // Medios / archivos
  { id: 'file',     icon: 'draft',         label: 'tool_file',     grupo: 'medios' },
  { id: 'map',      icon: 'map',           label: 'tool_map',      grupo: 'medios' },
  { id: 'carpeta',  icon: 'folder',        label: 'tool_carpeta',  grupo: 'medios' },
  { id: 'galeria',  icon: 'photo_library', label: 'tool_galeria',  grupo: 'medios' },
  // Estructura y herramientas
  { id: 'table',    icon: 'table_chart',   label: 'tool_table',    grupo: 'estructura' },
  { id: 'separator', icon: 'horizontal_rule', label: 'tool_separator', grupo: 'estructura' },
  { id: 'calendar', icon: 'calendar_month',label: 'tool_calendar', grupo: 'estructura' },
  { id: 'timer',    icon: 'timer',         label: 'tool_timer',    grupo: 'estructura' },
  { id: 'progreso', icon: 'donut_large',   label: 'tool_progreso', grupo: 'estructura' },
  { id: 'ruleta',   icon: 'pie_chart',     label: 'tool_ruleta',   grupo: 'estructura' },
];

// ── La concha del botón de More ──
// Pedida por el usuario: un caracol. Una espiral dentro de un círculo, con el
// trazo de la tinta de siempre. Se calcula una vez: una espiral de Arquímedes
// de dos vueltas y media, del centro hacia fuera.
const CONCHA_ESPIRAL = (() => {
  let d = '';
  const pasos = 64, vueltas = 2.5;
  for (let i = 0; i <= pasos; i++) {
    const f = i / pasos;
    const ang = f * vueltas * 2 * Math.PI - Math.PI / 2;
    const r = 0.8 + f * 7.6;
    d += (i ? ' L' : 'M') + (12 + r * Math.cos(ang)).toFixed(2) + ' ' + (12 + r * Math.sin(ang)).toFixed(2);
  }
  return d;
})();
function ConchaMas() {
  return (
    <svg className="mas-concha" viewBox="0 0 24 24" width="22" height="22" aria-hidden="true">
      <circle cx="12" cy="12" r="9.6" fill="none" stroke="currentColor" strokeWidth="1.7"/>
      <path d={CONCHA_ESPIRAL} fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

// Nombre de cada grupo de nodos, en el orden en que aparecen.
function nombresDeGrupo() {
  return {
    escribir: window.t('Escribir', 'Write'),
    medios: window.t('Medios', 'Media'),
    estructura: window.t('Estructura', 'Structure'),
    trazos: window.t('Trazos', 'Strokes'),
  };
}

// Para la hoja «Añadir» del teléfono: TODOS los nodos (la barra y los de
// más) en los cuatro grupos de la barra, en el mismo orden.
function gruposDeTodos() {
  const nombre = nombresDeGrupo();
  return Object.keys(nombre).map(grupo => ({
    grupo,
    nombre: nombre[grupo],
    // La carpeta solo existe en el escritorio (lee el disco con main.js).
    tools: TOOLS.concat(EXTRA_TOOLS).filter(tool => tool.grupo === grupo && (tool.id !== 'carpeta' || !!(window.electronAPI && window.electronAPI.carpetaElegir))),
  })).filter(g => g.tools.length);
}

// Los nodos de More en sus grupos: lo que se escribe, los medios y archivos,
// y la estructura.
function gruposDeMas() {
  const nombre = nombresDeGrupo();
  const grupos = [];
  EXTRA_TOOLS.forEach(tool => {
    const ultimo = grupos[grupos.length - 1];
    if (ultimo && ultimo.grupo === tool.grupo) ultimo.tools.push(tool);
    else grupos.push({ grupo: tool.grupo, nombre: nombre[tool.grupo] || '', tools: [tool] });
  });
  return grupos;
}

// El distintivo de cada miga: el cuadradito con el color del tablero o, en el
// inicio, una casa. El color es dato del usuario: entra como variable CSS.
function MarcaMiga({ c }) {
  return c.chipColor
    ? <span className="migas__chip" style={{ '--chip-color': c.chipColor }}/>
    : <span className="material-symbols-rounded migas__casa">home</span>;
}

// La hoja «Añadir» del teléfono. El tirador de arriba la deja bajar con el
// dedo: si se suelta pasada una distancia, se cierra; si no, vuelve.
function HojaNodos({ t, activeTool, onCierra, onElige, onToca }) {
  const [bajada, setBajada] = React.useState(0);
  const inicio = React.useRef(null);
  const pulsa = (e) => {
    inicio.current = e.clientY;
    try { e.currentTarget.setPointerCapture(e.pointerId); } catch (err) {}
  };
  const mueve = (e) => {
    if (inicio.current == null) return;
    setBajada(Math.max(0, e.clientY - inicio.current));
  };
  const suelta = () => {
    if (inicio.current == null) return;
    inicio.current = null;
    if (bajada > 90) onCierra();
    else setBajada(0);
  };
  return (
    <div className="hoja-nodos-fondo" onClick={onCierra}>
      <div
        className="hoja-nodos"
        role="dialog"
        aria-label={window.t('Añadir al lienzo', 'Add to the canvas')}
        style={bajada ? { transform: `translateY(${bajada}px)`, transition: 'none' } : undefined}
        onClick={(e) => e.stopPropagation()}
        onMouseDown={(e) => e.stopPropagation()}
      >
        <div className="hoja-tirador" onPointerDown={pulsa} onPointerMove={mueve} onPointerUp={suelta} onPointerCancel={suelta}>
          <span/>
        </div>
        <div className="hoja-nodos-cabeza">
          <b>{window.t('Añadir al lienzo', 'Add to the canvas')}</b>
          <button className="hoja-cerrar" onClick={onCierra} aria-label={window.t('Cerrar', 'Close')}>
            <span className="material-symbols-rounded">close</span>
          </button>
        </div>
        <div className="hoja-nodos-cuerpo">
          {gruposDeTodos().map(g => (
            <div className="hoja-nodos-grupo" key={g.grupo}>
              <div className="hoja-nodos-nombre">{g.nombre}</div>
              <div className="hoja-nodos-rejilla">
                {g.tools.map(tool => (
                  <button
                    key={tool.id}
                    className={`hoja-nodo press ${activeTool === tool.id ? 'active' : ''}`}
                    onMouseDown={(e) => onElige(e, tool.id)}
                    onClick={() => onToca(tool.id)}
                  >
                    <span className={`hoja-nodo-icono hoja-nodo-icono--${tool.grupo}`}>
                      <span className="material-symbols-rounded">{tool.icon}</span>
                    </span>
                    <span className="hoja-nodo-nombre">{t[tool.label] || tool.id}</span>
                  </button>
                ))}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function Topbar({
  lang, setLang,
  theme, setTheme,
  crumbs, onCrumb, onCrumbRename,
  activeTool, setActiveTool,
  onHome,
  onUndo, onRedo, canUndo, canRedo,
  onToolDragStart,
  updateAvailable, onUpdateClick,
  volume, onChangeVolume,
  onEstadoCompartir, estadoCompartir, estadoTitulo,
  onSettingsClick,
  userProfile, esPatrocinador, onAbrirCorona, onUserClick,
  onManualSync,
  isSyncingDrive,
  needsDriveAuth,
}) {
  const t = window.TRANSLATIONS[lang];
  const [extraOpen, setExtraOpen] = React.useState(false);
  // Cerrando: la tira se recoge hacia la concha antes de desaparecer.
  const [extraCerrando, setExtraCerrando] = React.useState(false);
  const cierraExtras = () => {
    if (!extraOpen || extraCerrando) return;
    if (esMovil()) { setExtraOpen(false); return; }
    setExtraCerrando(true);
    setTimeout(() => { setExtraOpen(false); setExtraCerrando(false); }, 260);
  };
  // Esc la recoge, como cualquier menú.
  React.useEffect(() => {
    if (!extraOpen) return;
    const tecla = (e) => { if (e.key === 'Escape') cierraExtras(); };
    window.addEventListener('keydown', tecla);
    return () => window.removeEventListener('keydown', tecla);
  });
  // Donde se dibuja el panel de extras. Va en coordenadas de pantalla porque
  // el panel se pinta fuera de la barra (que recorta), no dentro de ella.
  const [extraPos, setExtraPos] = React.useState({ left: 0, top: 0 });
  const extraBtnRef = React.useRef(null);

  // El alto de esta barra, para la barra de nodos puesta a un lado (Ajustes →
  // Barra de nodos): empieza justo debajo, y el alto cambia con la escala de
  // la interfaz y con la ventana fijada (ver styles.css, --alto-topbar).
  const topbarRef = React.useRef(null);
  React.useLayoutEffect(() => {
    const el = topbarRef.current;
    if (!el) return;
    const mide = () => document.documentElement.style.setProperty('--alto-topbar', Math.round(el.getBoundingClientRect().height) + 'px');
    mide();
    if (typeof ResizeObserver === 'undefined') return;
    const ro = new ResizeObserver(mide);
    ro.observe(el);
    return () => ro.disconnect();
  }, []);

  // El panel se coloca cuando ya esta en el DOM, con un efecto de disposicion:
  // se ejecuta despues de que React monte y antes de pintar, sin depender del
  // bucle de fotogramas. Ademas se encaja en la ventana, para que no se salga
  // por la derecha cuando el boton queda cerca del borde.
  React.useLayoutEffect(() => {
    if (!extraOpen) return;
    const ANCHO = 150; // ancho del panel mas su margen
    const coloca = () => {
      const el = extraBtnRef.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      // Con la barra de nodos a un lado o abajo (Ajustes), la tira sale hacia
      // donde hay sitio: a la derecha de la barra de la izquierda, a la
      // izquierda de la de la derecha, encima de la de abajo.
      const sitio = (!(window.odiIsMobile && window.odiIsMobile()) && document.documentElement.getAttribute('data-barra')) || 'top';
      if (sitio === 'left') {
        setExtraPos({ left: r.right + 10, top: Math.max(8, Math.min(r.top - 6, window.innerHeight - 260)), anchoMax: Math.max(220, window.innerWidth - r.right - 20), pico: 20, sitio });
        return;
      }
      if (sitio === 'right') {
        setExtraPos({ right: Math.max(8, window.innerWidth - r.left + 10), top: Math.max(8, Math.min(r.top - 6, window.innerHeight - 260)), anchoMax: Math.max(220, r.left - 20), pico: 20, sitio });
        return;
      }
      if (sitio === 'bottom') {
        setExtraPos({ right: Math.max(8, window.innerWidth - r.right), bottom: window.innerHeight - r.top + 10, anchoMax: Math.max(220, r.right - 8), pico: r.width / 2, sitio });
        return;
      }
      setExtraPos({
        left: Math.max(8, Math.min(r.left, window.innerWidth - ANCHO - 8)),
        top: r.bottom + 8,
        // La tira del caracol se ancla por la DERECHA, bajo la concha, y
        // crece hacia la izquierda. Si no cabe entera, se parte en dos filas.
        right: Math.max(8, window.innerWidth - r.right),
        anchoMax: Math.max(220, r.right - 8),
        pico: r.width / 2,
      });
    };
    coloca();
    // Y otra vez en el turno siguiente, por si el navegador movio la barra al
    // pulsar un boton que estaba a medias fuera (lo hace para traerlo a la
    // vista, y ocurre despues de este efecto). Es una precaucion barata.
    const t = setTimeout(coloca, 0);
    return () => clearTimeout(t);
  }, [extraOpen]);
  // En móvil la barra no cabe en una fila: las acciones de la derecha se
  // pliegan en un panel y las herramientas pasan a un raíl vertical.
  const [moreOpen, setMoreOpen] = React.useState(false);
  const [railOpen, setRailOpen] = React.useState(true);
  // ── Deslizador de la barra de nodos ──
  // Solo aparece cuando la barra esta REALMENTE cortada. En una pantalla ancha
  // no pinta nada y solo seria ruido, asi que se mide el desbordamiento de
  // verdad en vez de suponerlo por la anchura de la ventana: hay quien tiene la
  // ventana a medio monitor, o el zoom del sistema al 150%.
  const toolsRef = React.useRef(null);
  const [toolsCortada, setToolsCortada] = React.useState(false);
  const [toolsPos, setToolsPos] = React.useState(0);

  const midaToolsFn = React.useCallback(() => {
    const el = toolsRef.current;
    if (!el) return;
    const sobra = el.scrollWidth - el.clientWidth;
    // 4px de margen: sin el, un redondeo de medio pixel encendia el deslizador
    // en pantallas donde en realidad cabe todo.
    setToolsCortada(sobra > 4);
    setToolsPos(sobra > 0 ? el.scrollLeft / sobra : 0);
  }, []);

  React.useEffect(() => {
    const el = toolsRef.current;
    if (!el) return;
    midaToolsFn();
    const ro = new ResizeObserver(midaToolsFn);
    ro.observe(el);
    window.addEventListener('resize', midaToolsFn);
    return () => { ro.disconnect(); window.removeEventListener('resize', midaToolsFn); };
  }, [midaToolsFn, railOpen]);

  const muevaTools = (frac) => {
    const el = toolsRef.current;
    if (!el) return;
    el.scrollLeft = frac * (el.scrollWidth - el.clientWidth);
  };

  // Desplegable con el camino completo (lo abre el "…" de las migas plegadas).
  const [pathOpen, setPathOpen] = React.useState(false);
  const actual = crumbs[crumbs.length - 1] || { label: window.t('Inicio', 'Home') };
  // Migas que se ven: todas si son 4 o menos; si no, la primera, un "…" y las
  // dos últimas (el tablero anterior siempre queda a un clic).
  const migas = crumbs.length ? crumbs : [actual];
  const migasVisibles = migas.length <= 4
    ? migas.map((c, i) => ({ c, i }))
    : [{ c: migas[0], i: 0 }, { plegadas: true },
       { c: migas[migas.length - 2], i: migas.length - 2 },
       { c: migas[migas.length - 1], i: migas.length - 1 }];
  // Al cambiar de tablero se cierra solo: si no, se quedaria abierto
  // enseniando una ruta que ya no es la actual.
  React.useEffect(() => { setPathOpen(false); }, [crumbs.length, actual.id]);

  const goBack = () => {
    if (crumbs.length > 1) onCrumb(crumbs.length - 2);
    else onHome();
    window.playAudioTone && window.playAudioTone('click');
  };

  const startToolDrag = (e, toolId) => {
    if (e.button !== 0) return;
    if (!onToolDragStart) return;
    // Pass through to canvas: it will track drag and create on drop
    onToolDragStart(e, toolId);
  };

  return (
    // data-mueve-ventana: con la ventana fijada (sin barra de título), las
    // zonas vacías de esta barra la mueven. Ver Ventana.jsx.
    <div className="topbar" data-mueve-ventana="" ref={topbarRef}>
      <button className="brand press" onClick={onHome} title={t.home}>
        <div className="brand-mark"><window.BrandMark/></div>
      </button>
      {/* Retroceder un nivel. Antes solo estaba en móvil, porque en escritorio
          se subía pulsando la miga anterior de la cadena. Al desaparecer esa
          cadena hace falta en todas partes: es el gesto de subir un nivel de un
          solo clic, sin abrir el desplegable de la ruta. */}
      {crumbs.length > 1 && (
      <button
        className="crumb-back"
        onClick={goBack}
        title={window.t('Volver al nivel anterior', 'Back one level')}
        aria-label={window.t('Volver', 'Back')}
      >
        <span className="material-symbols-rounded">arrow_back</span>
      </button>
      )}
      {/* Ruta actual, como migas de pan: cada tablero del camino es un botón y
          se vuelve a él de un solo clic. Antes era una pastilla con el camino en
          un desplegable, y subir dos niveles costaba varios clics. Para que no
          crezca sin límite con tableros muy anidados, si hay más de 4 niveles
          los del medio se pliegan en un "…" que abre el camino completo. */}
      <nav className="migas" aria-label={window.t('Dónde estás', 'Where you are')}>
        {migasVisibles.map((m, k) => (
          <React.Fragment key={m.plegadas ? 'plegadas' : (m.c.id || m.i)}>
            {k > 0 && <span className="material-symbols-rounded migas__sep" aria-hidden="true">chevron_right</span>}
            {m.plegadas ? (
              <button
                className={`migas__miga migas__miga--plegadas ${pathOpen ? 'migas__miga--abierta' : ''}`}
                onClick={() => { setPathOpen(o => !o); window.playAudioTone && window.playAudioTone('click'); }}
                title={crumbs.map(c => c.label).join('  ›  ')}
                aria-label={window.t('Ver el camino completo', 'Show the full path')}
              >…</button>
            ) : m.i === migas.length - 1 ? (
              <span className="migas__miga migas__miga--actual" title={m.c.label} aria-current="page">
                <MarcaMiga c={m.c}/>
                <span className="migas__texto">{m.c.label}</span>
              </span>
            ) : (
              <button
                className="migas__miga"
                onClick={() => { onCrumb(m.i); window.playAudioTone && window.playAudioTone('click'); }}
                title={m.c.label}
              >
                <MarcaMiga c={m.c}/>
                <span className="migas__texto">{m.c.label}</span>
              </button>
            )}
          </React.Fragment>
        ))}

        {pathOpen && (
          <>
            <div className="migas__fondo" onClick={() => setPathOpen(false)}/>
            <div className="migas__menu">
              <div className="migas__menu-titulo">{window.t('Dónde estás', 'Where you are')}</div>
              {crumbs.map((c, i) => (
                <button
                  key={c.id || i}
                  className={`migas__menu-item ${i === crumbs.length - 1 ? 'migas__menu-item--actual' : ''}`}
                  style={{ '--nivel': Math.min(i, 6) }}
                  onClick={() => { setPathOpen(false); onCrumb(i); }}
                  title={c.label}
                >
                  <MarcaMiga c={c}/>
                  <span className="migas__texto">{c.label}</span>
                  {i === crumbs.length - 1 && (
                    <span className="material-symbols-rounded migas__aqui">my_location</span>
                  )}
                </button>
              ))}
            </div>
          </>
        )}
      </nav>

      <div className="topbar-spacer" data-mueve-ventana=""/>

      {/* Muestra u oculta el raíl de herramientas (solo visible en móvil, donde
          el raíl ocupa el borde derecho de la pantalla). */}
      <button
        className={`tools-fab ${railOpen ? 'open' : ''}`}
        onClick={() => { setRailOpen(o => !o); window.playAudioTone && window.playAudioTone('click'); }}
        title={railOpen ? window.t('Ocultar herramientas', 'Hide tools') : window.t('Mostrar herramientas', 'Show tools')}
        aria-label={window.t('Herramientas', 'Tools')}
      >
        <span className="material-symbols-rounded">{railOpen ? 'close' : 'widgets'}</span>
      </button>

      <div className="tools-wrap">
      <div
        className={`tools ${railOpen ? 'rail-open' : 'rail-closed'}`}
        ref={toolsRef}
        onScroll={midaToolsFn}
      >
        {TOOLS.map((tool, idx) => (
          <React.Fragment key={tool.id}>
            {idx === 4 && <div className="tool-divider"/>}
            {idx === 8 && <div className="tool-divider"/>}
            {idx === 12 && <div className="tool-divider"/>}
            <button
              className={`tool press ${activeTool === tool.id ? 'active' : ''}`}
              data-tool={tool.id}
              title={`${t[tool.label] || tool.id} · ${window.t('Arrastra al canvas o clic', 'Drag to canvas or click')}`}
              onMouseDown={(e)=>startToolDrag(e, tool.id)}
              onClick={() => window.playAudioTone && window.playAudioTone('click')}
            >
              <div className={`tool-icon tool-icon--${tool.grupo}`}>
                <span className="material-symbols-rounded">{tool.icon}</span>
              </div>
              <div className="tool-label">{t[tool.label] || tool.id}</div>
            </button>
          </React.Fragment>
        ))}

        {/* More: una concha de caracol. Al pulsarla da una vuelta y de ella sale
            la tira con el resto de nodos (ver el portal de más abajo). */}
        <button
          ref={extraBtnRef}
          className={`tool press mas-boton ${extraOpen && !extraCerrando ? 'active abierto' : ''}`}
          title={window.t('Más herramientas', 'More tools')}
          onClick={() => {
            if (extraOpen) cierraExtras(); else setExtraOpen(true);
            window.playAudioTone && window.playAudioTone('click');
          }}
        >
          <div className="tool-icon tool-icon--medios">
            <ConchaMas/>
          </div>
          <div className="tool-label">{window.t('Más', 'More')}</div>
        </button>
      </div>

      {/* Deslizador: solo cuando la barra esta cortada de verdad. Si cabe todo,
          no se dibuja — no es una barra de scroll permanente. */}
      {toolsCortada && (
        <input
          className="tools-slider"
          type="range"
          min="0" max="1000" step="1"
          value={Math.round(toolsPos * 1000)}
          onChange={(e) => {
            // El pulgar se mueve con lo que dice el propio deslizador, sin
            // esperar al evento de scroll de la barra: asi el control responde
            // aunque el navegador agrupe o retrase ese evento.
            const frac = Number(e.target.value) / 1000;
            setToolsPos(frac);
            muevaTools(frac);
          }}
          title={window.t('Desplazar las herramientas', 'Scroll the tools')}
          aria-label={window.t('Desplazar las herramientas', 'Scroll the tools')}
        />
      )}
      </div>

      {/* Panel de herramientas extras.
          Va con un portal al final de la página, FUERA de la barra. La barra
          recorta lo que se sale de ella (hace falta para poder desplazarla), y
          este panel cuelga por debajo: dentro quedaba cortado y no se veía
          nada al pulsar "Más". Al estar fuera, se coloca en coordenadas fijas
          calculadas desde el botón. */}
      {extraOpen && !esMovil() && ReactDOM.createPortal(
        <>
          {/* Mientras se recoge ya no tapa nada: el clic que llega en ese cuarto
              de segundo es para el lienzo (colocar el nodo elegido, por ejemplo). */}
          {!extraCerrando && (
            <div
              style={{ position: 'fixed', inset: 0, zIndex: 400 }}
              onClick={cierraExtras}
            />
          )}
          {/* La tira del caracol. Sale de debajo de la concha y se estira hacia
              la izquierda; los nodos van de izquierda a derecha, agrupados por
              su color, y entran uno detrás de otro desde la concha con un
              pequeño vaivén, como el cuerpo de una serpiente al estirarse. */}
          <div
            className={`mas-serpiente ${extraCerrando ? 'cierra' : ''}`}
            style={{ position: 'fixed', ...(extraPos.sitio === 'left' ? { left: extraPos.left, top: extraPos.top } : extraPos.sitio === 'bottom' ? { right: extraPos.right, bottom: extraPos.bottom } : { right: extraPos.right, top: extraPos.top }), maxWidth: extraPos.anchoMax, '--pico': (extraPos.pico || 20) + 'px' }}
            onMouseDown={(e) => e.stopPropagation()}
          >
            {(() => {
              const grupos = gruposDeMas();
              const total = EXTRA_TOOLS.length;
              let k = 0;
              return grupos.map(g => (
                <div className="mas-grupo" key={g.grupo}>
                  <div className="mas-grupo-nombre">{g.nombre}</div>
                  <div className="mas-grupo-nodos">
                    {g.tools.map(tool => {
                      const j = k++;
                      // Los de la derecha salen antes: la tira nace en la concha.
                      const retraso = 60 + (total - 1 - j) * 32;
                      const ola = Math.sin(j * 1.1) * 12;
                      return (
                        <button
                          key={tool.id}
                          className={`tool press mas-nodo ${activeTool === tool.id ? 'active' : ''}`}
                          style={{ '--retraso': retraso + 'ms', '--ola': ola.toFixed(1) + 'px' }}
                          title={`${t[tool.label] || tool.id} · ${window.t('Arrastra al canvas o clic', 'Drag to canvas or click')}`}
                          onMouseDown={(e) => {
                            cierraExtras();
                            startToolDrag(e, tool.id);
                          }}
                          onClick={() => {
                            window.playAudioTone && window.playAudioTone('click');
                            setActiveTool(tool.id);
                          }}
                        >
                          <div className={`tool-icon tool-icon--${tool.grupo}`}>
                            <span className="material-symbols-rounded">{tool.icon}</span>
                          </div>
                          <div className="tool-label">{t[tool.label] || tool.id}</div>
                        </button>
                      );
                    })}
                  </div>
                </div>
              ));
            })()}
          </div>
        </>,
        document.body
      )}

      {/* En el teléfono, «Más» abre una hoja desde abajo con TODOS los nodos
          por grupos de color, a tamaño de dedo (como la de Freeform en el
          iPhone). Se cierra tocando fuera, con la X o bajándola por el
          tirador. Elegir uno es lo mismo que en la barra: luego se toca el
          lienzo donde va. */}
      {extraOpen && esMovil() && ReactDOM.createPortal(
        <HojaNodos
          t={t}
          activeTool={activeTool}
          onCierra={() => setExtraOpen(false)}
          onElige={(e, toolId) => { setExtraOpen(false); startToolDrag(e, toolId); }}
          onToca={(toolId) => { setExtraOpen(false); window.playAudioTone && window.playAudioTone('click'); setActiveTool(toolId); }}
        />,
        document.body
      )}

      {/* Deshacer y rehacer, a la vista en el teléfono (en el escritorio están
          con las demás acciones). Antes vivían dentro del panel ⋯: dos toques
          para lo que más se usa. */}
      <button
        className="solo-movil movil-deshacer"
        onClick={() => { onUndo && onUndo(); window.playAudioTone && window.playAudioTone('click'); }}
        disabled={!canUndo}
        aria-label={window.t('Deshacer', 'Undo')}
        title={window.t('Deshacer', 'Undo')}
      >
        <span className="material-symbols-rounded">undo</span>
      </button>
      <button
        className="solo-movil movil-rehacer"
        onClick={() => { onRedo && onRedo(); window.playAudioTone && window.playAudioTone('click'); }}
        disabled={!canRedo}
        aria-label={window.t('Rehacer', 'Redo')}
        title={window.t('Rehacer', 'Redo')}
      >
        <span className="material-symbols-rounded">redo</span>
      </button>

      {/* Botón "⋯": en móvil despliega todo lo que sigue como panel; en
          escritorio está oculto y .topbar-tail usa display:contents, así que la
          barra se ve exactamente igual que antes. */}
      <button
        className="topbar-more"
        onClick={() => { setMoreOpen(o => !o); window.playAudioTone && window.playAudioTone('click'); }}
        title={window.t('Más opciones', 'More options')}
        aria-label={window.t('Más opciones', 'More options')}
      >
        <span className="material-symbols-rounded">{moreOpen ? 'expand_less' : 'more_vert'}</span>
      </button>
      {moreOpen && <div className="topbar-tail-scrim" onClick={() => setMoreOpen(false)}/>}

      <div className={`topbar-tail ${moreOpen ? 'open' : ''}`}>
      <div className="topbar-spacer" data-mueve-ventana="" style={{ display: 'flex', justifyContent: 'flex-end', alignItems: 'center' }}>
        <button
          className="feedback-topbar-btn"
          onClick={() => window.open('https://github.com/Neuroxcx1/Odinote/discussions', '_blank', 'noopener,noreferrer')}
          title={window.t('Comenta o danos tu feedback en GitHub', 'Give us feedback or suggestions on GitHub')}
        >
          <span className="material-symbols-rounded" style={{ fontSize: '15px' }}>forum</span>
          <span>{window.t('Feedback', 'Feedback')}</span>
        </button>
      </div>

      <div className="topbar-actions">
        <button
          className="icon-btn lift"
          title={needsDriveAuth
            ? window.t('El acceso a Drive caducó: haz clic para renovarlo', 'Drive access expired: click to renew it')
            : window.t('Sincronizar con Google Drive ahora', 'Sync with Google Drive now')}
          onClick={() => { onManualSync && onManualSync(); window.playAudioTone && window.playAudioTone('click'); }}
          style={{ marginRight: 6, position: 'relative' }}
        >
          <span
            className="material-symbols-rounded"
            style={isSyncingDrive ? { animation: 'spin 1.5s linear infinite', color: 'var(--olive-texto)' } : (needsDriveAuth ? { color: 'var(--wine-texto)' } : undefined)}
          >
            {needsDriveAuth ? 'sync_problem' : 'sync'}
          </span>
          {needsDriveAuth && (
            <span style={{ position: 'absolute', top: '4px', right: '4px', width: '7px', height: '7px', borderRadius: '50%', background: 'var(--wine, #E6544F)', border: '1.5px solid var(--paper)' }}/>
          )}
        </button>
        {window.electronAPI && (
          <button
            className={`icon-btn lift update-bell-btn ${updateAvailable ? 'has-update' : ''}`}
            style={{
              color: updateAvailable ? 'var(--wine, #E6544F)' : 'var(--text-soft, #595459)',
              marginRight: 6,
              animation: updateAvailable ? 'pulse-bell 1.5s infinite alternate' : 'none'
            }}
            onClick={() => { onUpdateClick(); window.playAudioTone && window.playAudioTone('click'); }}
            title={
              updateAvailable
                ? (window.t('¡Nueva actualización disponible! Haz clic para descargar de GitHub.', 'New update available! Click to download from GitHub.'))
                : (window.t('Buscar actualizaciones', 'Check for updates'))
            }
          >
            <span className="material-symbols-rounded">
              {updateAvailable ? 'notifications_active' : 'notifications'}
            </span>
          </button>
        )}
        <button
          className="icon-btn lift"
          title={window.t('Deshacer', 'Undo') + ' (' + ((window.shortcuts && window.shortcuts.undo && window.shortcuts.undo.label) || 'Ctrl + Z') + ')'}
          onClick={() => { onUndo(); window.playAudioTone && window.playAudioTone('click'); }}
          style={{ opacity: canUndo ? 1 : 0.4 }}
        >
          <span className="material-symbols-rounded">undo</span>
        </button>
        <button
          className="icon-btn lift"
          title={window.t('Rehacer', 'Redo') + ' (' + ((window.shortcuts && window.shortcuts.redo && window.shortcuts.redo.label) || 'Ctrl + Y') + ')'}
          onClick={() => { onRedo(); window.playAudioTone && window.playAudioTone('click'); }}
          style={{ opacity: canRedo ? 1 : 0.4 }}
        >
          <span className="material-symbols-rounded">redo</span>
        </button>

        {/* Online / Offline y compartir.
            Vivía abajo del todo, en la fila de pastillas de estado junto a
            "Guardado" y "2 nodos" — información de fondo que nadie mira. Pero
            no es un dato: es el botón que abre el compartir y las sesiones en
            vivo. Aquí arriba, con el resto de acciones, se ve y se pulsa. */}
        {onEstadoCompartir && (
          <button
            className={`odi-share-pill ${estadoCompartir}`}
            title={estadoTitulo}
            onClick={() => { onEstadoCompartir(); window.playAudioTone && window.playAudioTone('click'); }}
          >
            <span className="material-symbols-rounded">
              {estadoCompartir === 'invitado' ? 'sensors'
                : estadoCompartir === 'offline' ? 'cloud_off'
                : estadoCompartir === 'caido' ? 'cloud_alert'
                : 'cloud_done'}
            </span>
            <span>
              {estadoCompartir === 'invitado' ? window.t('Invitado', 'Guest')
                : estadoCompartir === 'offline' ? window.t('Compartir', 'Share')
                : estadoCompartir === 'caido' ? window.t('Sin conexión', 'Disconnected')
                : window.t('Online', 'Online')}
            </span>
          </button>
        )}

        <div className="volume-ctrl" style={{ marginRight: 6 }}>
          <button
            className="icon-btn-mute"
            title={volume === 0 ? (window.t('Activar sonido', 'Unmute')) : (window.t('Silenciar', 'Mute'))}
            onClick={() => {
              let nextVol = 0.5;
              if (volume > 0) {
                localStorage.setItem('odinote.last_volume', volume.toString());
                nextVol = 0;
              } else {
                const last = localStorage.getItem('odinote.last_volume');
                nextVol = last ? parseFloat(last) : 0.5;
              }
              onChangeVolume(nextVol);
              setTimeout(() => { window.playAudioTone && window.playAudioTone('click'); }, 20);
            }}
          >
            <span className="material-symbols-rounded">
              {volume === 0 ? 'volume_off' : (volume < 0.4 ? 'volume_down' : 'volume_up')}
            </span>
          </button>
          <input
            type="range"
            min="0"
            max="1"
            step="0.05"
            value={volume}
            onChange={(e) => onChangeVolume(parseFloat(e.target.value))}
            className="volume-slider"
            title={`${Math.round(volume * 100)}%`}
          />
        </div>
        {/* El estado "sincronizando" se muestra girando el botón ↻ (arriba), sin
            insertar texto que desplace toda la barra hacia la izquierda */}
        <window.CoronaBoton activo={!!esPatrocinador} onAbrir={onAbrirCorona} />
        <button
          className={`icon-btn lift user-profile-btn ${userProfile ? 'has-name' : 'no-name'}`}
          title={userProfile ? `${window.t('Perfil de', 'Profile of')} ${userProfile.name}` : window.t('Iniciar sesión con Google (Requerido para colaborar)', 'Sign in with Google (Required to collaborate)')}
          onClick={() => { onUserClick && onUserClick(); window.playAudioTone && window.playAudioTone('click'); }}
          style={{ marginRight: '4px', position: 'relative' }}
        >
          <span className="material-symbols-rounded" style={{ color: userProfile ? 'var(--olive-texto)' : 'inherit' }}>person</span>
          {!userProfile && (
            <span style={{
              position: 'absolute',
              top: '4px',
              right: '4px',
              width: '6px',
              height: '6px',
              borderRadius: '50%',
              background: 'var(--wine, #E6544F)',
              border: '1.5px solid var(--paper)'
            }}/>
          )}
        </button>
        <button
          className="icon-btn lift settings-btn"
          title={window.t('Ajustes', 'Settings')}
          onClick={() => { onSettingsClick && onSettingsClick(); window.playAudioTone && window.playAudioTone('click'); }}
          style={{ marginRight: '4px' }}
        >
          <span className="material-symbols-rounded">settings</span>
        </button>
        <button
          className="icon-btn lift theme-btn"
          title={theme==='dark' ? window.t('Modo claro', 'Light mode') : window.t('Modo oscuro', 'Dark mode')}
          onClick={() => { setTheme(theme === 'dark' ? 'light' : 'dark'); window.playAudioTone && window.playAudioTone('click'); }}
        >
          <span className="material-symbols-rounded">{theme === 'dark' ? 'light_mode' : 'dark_mode'}</span>
        </button>
        <div
          className="lang-switch"
          title={
            {
              es: 'Español',
              en: 'English',
              fr: 'Français',
              de: 'Deutsch',
              it: 'Italiano',
              pt: 'Português',
              zh: '中文 (Chinese)',
              ja: '日本語 (Japanese)',
              ko: '한국어 (Korean)',
              ar: 'العربية (Arabic)',
              ru: 'Русский (Russian)'
            }[lang] || ''
          }
          style={{ position: 'relative' }}
        >
          <span style={{ pointerEvents: 'none', position: 'absolute', left: '12px', top: '50%', transform: 'translateY(-50%)', color: 'var(--ink)', fontSize: '11.5px', fontWeight: '700' }}>
            {lang.toUpperCase()}
          </span>
          <select
            value={lang}
            onChange={(e) => { setLang(e.target.value); window.playAudioTone && window.playAudioTone('click'); }}
            className="lang-select"
            style={{ opacity: 0, cursor: 'pointer', position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', zIndex: 2 }}
          >
            <option value="es" title="Español">ES - Español</option>
            <option value="en" title="English">EN - English</option>
            <option value="fr" title="Français">FR - Français</option>
            <option value="de" title="Deutsch">DE - Deutsch</option>
            <option value="it" title="Italiano">IT - Italiano</option>
            <option value="pt" title="Português">PT - Português</option>
            <option value="zh" title="中文 (Chinese)">ZH - 中文 (Chinese)</option>
            <option value="ja" title="日本語 (Japanese)">JA - 日本語 (Japanese)</option>
            <option value="ko" title="한국어 (Korean)">KO - 한국어 (Korean)</option>
            <option value="ar" title="العربية (Arabic)">AR - العربية (Arabic)</option>
            <option value="ru" title="Русский (Russian)">RU - Русский (Russian)</option>
          </select>
        </div>
      </div>
      </div>
    </div>
  );
}

window.Topbar = Topbar;
window.TOOLS = [...TOOLS, ...EXTRA_TOOLS];
