MF-13 · sugerencias · sep 2026

Mobile · sugerencias, anuncios y ayuda

La esquina que escucha, ahora en el bolsillo.

Las piezas con las que Mogos le habla al cliente — y lo escucha — llegan a la app, a paridad con apps/client. El FAB con el monograma flota sobre la tab bar y abre el panel a pantalla completa: saludo de lanzamiento, chips para empezar, hilos con respuesta del equipo y una línea que dice «Vista por el equipo» sin refrescar (broadcast por usuario). Los anuncios toman su color del type y su superficie del placement: top banner que empuja el shell, modal que adapta el ancho al contenido embebido, tarjeta inline en el Inicio y pantalla de mantenimiento que bloquea todo. La ayuda se muda a Más: contacto y novedades; el spotlight de herramientas queda diferido.

Superficies
widget · 4 anuncios · ayuda en Más
Paridad
estricta · el código shippeado = verdad
Realtime
broadcast suggestions-user-{id}
Persistencia
MMKV · espejo de localStorage
Fuera
atajos · cookie · admin · WhatsApp · discovery
Pantallas
15 a 390×844

01La esquina que escucha

suggestions-fab.tsx · suggestions-widget.tsx · use-suggestions-unread.ts

El botón es el monograma sobre su aurora (--grad-hero), la misma voz que las puertas de auth. En la web móvil mide 48 px y descansa a la derecha, encima de la tab bar; la app re-deriva la altura para el shell de MF-03: la tab bar mide 98, así que el botón vive a 98 + 12 + safe-area del fondo, en su propia columna — el orbe de Crear (--grad-marca) vive dentro de la barra y nunca se tocan. Se monta una sola vez en el shell autenticado y flota sobre todas las tabs y stacks, solo con sesión iniciada. En reposo está quieto: sin halo, sin pulso, sin insignia. No pide nada.

  1. El monograma es el botón

    48 px sobre --grad-hero, borde rgba(170,228,243,.35) y la sombra del widget web. El vector viene de logo-source: la «m» recolorea a blanco y el punto rojo no se toca. Sin label en móvil (el «Cuéntanos algo» del hover es de escritorio).

  2. Columna propia, plano propio

    right: 14 · bottom: tab bar (98) + 12 + safe-area. La web usa 74 sobre tabs de 62 — misma regla, shell distinto. El orbe de Crear queda centrado dentro de la barra: el pulgar sabe cuál es cuál sin pensarlo. El clearance de 88 px de la web lo cubre el padding-bottom que las pantallas ya llevan.

  3. Con novedad: halo + insignia

    El halo celeste respira 3.2 s (escala 1 → 1.26, se apaga al 70 %). La insignia es celeste con dígitos marino y borde blanco de 2 px, arriba a la derecha — nunca sobre el punto rojo. Más de 9 → «9+». Aria: «{{count}} novedades sin leer». Reduced motion: el halo no se pinta; la insignia queda.

  4. Qué cuenta como novedad

    countSuggestionNews: un cambio de estado (visto / anotada / lista) después del último openedAt + cada respuesta del equipo posterior. openedAt se guarda en MMKV (espejo de mogos.suggestions.openedAt.{userId}) y se estampa al abrir el panel — con el corte capturado antes, para que los puntos no mueran al abrir.

  5. Siempre escuchando

    El realtime se suscribe en la raíz del widget, no en el panel: el canal broadcast suggestions-user-{id} (evento suggestions-changed) invalida ['suggestions'] y la insignia se actualiza con el panel cerrado. En background el socket muere: se reconcilia con un refetch al volver a foreground (AppState).

Hay novedad halo 3.2 s + insignia celeste, fuera del punto rojo
Las otras puertas ya cableadas desde MF-03

«Enviar sugerencia» (Más › Accesos) y «Reportar problema» (Crear › Otros) existen desde MF-03 y en la web móvil llevan a la página /suggestions; Fabrizio decidió (2026-09-24) que en la app abren el panel del widget — la sección 03 lo detalla; la página legacy no se construye. El FAB es la puerta nueva y es global: no depende de ninguna fila. La web además llega por el ToolsGroup del top bar de escritorio, que en móvil no existe.

02El panel, a pantalla completa

suggestions-panel.tsx · crown · composer · thread-list · rating-card · status-line

A 390 el panel es pantalla completa, no una hoja a medias — una hoja al 70 % deja media app asomando y el teclado se come lo que queda. Arriba la corona: --grad-hero con dos brillos a la deriva (celeste 22 s, lima 31 s) y un saludo que se elige por prioridad: recién enviada («Gracias, {{nombre}}») > ventana de lanzamiento (activa hasta el 1 oct 2026) > default («¿Qué mejoramos?»). El cuerpo es lienzo blanco. La vista inicial la decide resolveInitialView: sin sugerencias → nueva; una → su hilo; dos o más → la lista «Historial». Se resuelve una vez — los refetch nunca te cambian de vista.

  1. La corona saluda una sola cosa

    Título 15/600 y cuerpo 12.5 en --mogos-on-marino-soft, con la X (32 px) arriba a la derecha («Cerrar sugerencias»). El copy es el shippeado, no el de la proposal web vieja. «Recién enviada» gana mientras haya un mensaje en vuelo que no falló.

  2. Chips solo la primera vez

    «Para empezar» aparece únicamente en la primera sugerencia de la vida y sin nada en vuelo. Cada chip (44 px, tile celeste de 26) entra en cascada — 50/130/210 ms — y al tocarlo prellena y enfoca: nunca clasifica ni envía.

  3. El composer es una píldora

    Hueso con borde --line en reposo; al teclear pasa a blanco con borde celeste-deep y anillo rgba(95,185,216,.18). La letra va a 16 px para que iOS no haga zoom. Crece hasta 96 px; Enter envía. Validación: mínimo 10 («Cuéntanos un poco más…»), máximo 500. El botón (36, marino) se apaga a --paper sin texto válido. La nota «Lo lee el equipo…» es de escritorio: aquí no va.

  4. Envío optimista

    La burbuja aparece al instante con «Enviando…» y el POST /suggestions sale detrás (con route de la pantalla de origen). Si falla: «No se envió» + «Reintentar». Si el usuario cierra con un mensaje retenido, flush() lo envía al desmontar. La burbuja local muere cuando la fila del server aparece en /mine — y el panel salta a su hilo.

  5. No es modal

    Sin backdrop ni focus trap en web; en la app es una pantalla presentada sobre el shell (se cierra con la X o el gesto de volver). El estado nace del botón: 640 ms ease-out desde la esquina inferior derecha, radio 50 % → 20. Reduced motion: aparece resuelta.

Recién enviada «Gracias, {{nombre}}» + rating una sola vez
El hilo estado «Lista» + respuesta del equipo, sin caja
El historial dos o más sugerencias abren aquí

La fila muestra el último que habló: si fuiste tú, el preview lleva el prefijo «Tú:» en gris. La hora es hh:mm el mismo día y «12 sept» después. El punto celeste (8 px con aro rgba(170,228,243,.25)) marca la fila con novedades; se ordena por última actividad. El rating aparece solo tras el primer mensaje de la vida y una vez por sesión: retiene el envío («Enviando…») hasta que eliges o saltas, espera 1200 ms por si cambias, y viaja en el mismo POST. Estrellas en --mogos-warning-deep con pop de resorte — uno de los dos únicos resortes del widget; el otro es el tick de «Lista».

Cargando mientras llega /suggestions/mine
Error con reintento en el mismo plano

03Las puertas abren el panel

create-items.ts · mobile-create-sheet.tsx · mas.tsx · decidido 2026-09-24

Decidido por Fabrizio (2026-09-24): «Enviar sugerencia» (Más › Accesos) y «Reportar problema» (Crear › Otros) abren el panel del widget — el sistema vivo, con respuestas del equipo desde Slack y la línea de estado — y no la página vieja. El formulario legacy /suggestions (rating obligatorio, escalas brand/gray) no se porta: en la app hay una sola voz para escuchar al cliente. El rating no se pierde — vive en el widget, tras el primer mensaje (sección 02).

  1. Dos filas, un panel

    Ambas puertas navegan al panel del widget (vista según resolveInitialView) y estampan su origen en el route del POST /suggestions — «Reportar problema» llega como el Crear sheet, la fila de Más como Más — para que el equipo sepa desde dónde escribió el cliente.

  2. La página legacy no se construye

    Sin formulario aparte, sin rating obligatorio, sin la nota de las 24-48 horas: el widget responde más rápido de lo que esa nota promete. La web conserva su página; la app tiene una sola voz.

  3. Los textos no cambian

    Las filas conservan sus claves y sus íconos de MF-03 («Enviar sugerencia», «Reportar problema»): cambia el destino, no el copy. El FAB sigue siendo la puerta global y no depende de ninguna fila.

04Anuncios: color por type, superficie por placement

announcement-provider · top-banner · announcement-modal · embed · floating · maintenance

Un AnnouncementProvider nativo con las reglas de la web: GET announcements/active (60 s de staleTime, sin polling — refresca al enfocar y al reconectar), un anuncio por superficie (el primero por displayOrder; descartar el primero revela el siguiente), y si hay un anuncio de mantenimiento, suprime todo lo demás. El registro no trae colores: el type decide el color y el placement la superficie. El «visto» se guarda en MMKV con scope de usuario (mogos.announcements.dismissed.{userId}) — decisión de Fabrizio 2026-09-24: mejor que la web, donde es por navegador y eterno.

  1. Abre 500 ms después

    El modal espera medio segundo tras montar la pantalla — que el shell respire antes de hablar. Velo --mogos-marino/70, card blanca con radio 10, texto centrado en móvil, fade + zoom 95 %.

  2. El ancho lo decide el contenido… en escritorio

    16:9 → 640 · 4:5 → 480 · 9:16 → 400. A 390 los tres convergen al ancho útil (pantalla − 16 por lado) y el alto manda: max-h 92% con scroll interno para los reels altos.

  3. Dos cierres, un destino

    La X de arriba y el «Cerrar» del pie hacen lo mismo, y cualquier cierre — botón, X, gesto — marca el anuncio como visto para ese usuario — el modal habla una sola vez. El footer es col-reverse: el CTA (si el admin puso texto y URL) queda arriba, a ancho completo.

  4. El embed viaja en WebView

    YouTube /embed/{id}?rel=0, Instagram /p|/reel → /embed/, TikTok embed/v2, Vimeo player — detección por URL en el cliente, sin tokens. Archivo directo (.mp4/.webm/.ogg) → expo-video. Caja negra sin spinner, 6 s de timeout, y el riesgo del brief (IG/TikTok dentro de WebView móvil) se prueba en la F1 con los dos embeds reales.

  5. El título en display

    DialogTitle usa font-family-display: Lemon Milk 24, centrado. La descripción es el content del anuncio en 14/20 gris.

Reel 9:16 vertical, se siente nativo
La escalera el embed falló: card con miniatura
Top banner 36 px que empujan el shell
La tarjeta del Inicio FLOATING_BOTTOM en móvil · decidido 2026-09-24

Debajo del header, scrollea con el contenido (el resto del Inicio va en esqueleto: es de MF-03 y no se redibuja). Tinte soft del type con el acento fuerte en el borde izquierdo, título en bold, texto, CTA como link y la X arriba a la derecha. Una a la vez — la más reciente —; cerrarla la despide para ese usuario. Nunca pisa la tab bar ni el FAB.

Mantenimiento bloquea la app, sin cierre
TypeEtiquetaColor (banners)ContrasteTarjeta en Inicio · soft + acento
URGENTUrgentebg-mogos-rojo #C9313C · texto blancoAArojo-soft · acento rojo-deep
INFOInformativobg-mogos-marino #021B54 · texto blancoAAAceleste-soft · acento marino
WARNINGAdvertenciabg-mogos-warning-deep #8A5A10 · texto blancoAA (el base #B6791A da 3.71:1 y no llega)warning-soft · acento warning-deep
SUCCESSExitosobg-mogos-success #2F7A4F · texto blancoAAsuccess-soft · acento success-deep
MAINTENANCEMantenimientobg-mogos-marino-deep #01123A · texto blancoAAApaper · acento marino-deep

El top banner muestra en móvil solo el título truncado (el content es de escritorio) más el CTA y la X; empuja el shell — el equivalente nativo del --top-banner-height que en web desplaza top bar y tabs, siempre bajo el status bar (safe-area). El FLOATING_BOTTOM no flota en móvil: es la tarjeta del Inicio. El modal y la pantalla de mantenimiento no usan el mapa de colores. Cuando el embed falla sin miniatura propia, el server scrapea el og:image (announcements/og-preview, allowlist de hosts, caché 6 h) y si tampoco hay, queda el peldaño 3: solo el botón «Ver en {Proveedor}». El provider pide hoy ALL+ANONYMOUS — el TODO web de pedir CLIENT se replica y queda anotado como deuda. El copy de todas estas superficies está hardcodeado en la web: la app lo lleva a claves i18n nuevas es/en/zh con el español literal.

05La ayuda se muda a Más

sidebar-help-button.tsx · lib/changelog.ts · use-changelog-state.ts · tools-discovery

En la web la ayuda vive en un dropdown del sidebar de escritorio; ningún shell móvil la tiene. Del menú real se porta lo que existe y funciona: «Contactar soporte» (mailto:soporte@mogos.com — sin WhatsApp: no está en la ayuda web), «Enviar sugerencia» (ya está en Accesos desde MF-03) y «Novedades» (la lista del changelog, 3 entradas sin fechas, con su punto azul de no-visto). Quedan fuera, por paridad: «Buscar ayuda...» (deshabilitado), los atajos de teclado (no aplican), «Ver todo →» (muerto) y Tutoriales (oculto desde 26bf55375). La forma — una sección «Ayuda» en Más y las novedades en una hoja — quedó aprobada por Fabrizio (2026-09-24). MF-13 solo agrega la sección Ayuda — banda, Cuenta, Accesos, Preferencias, Cerrar sesión y versión quedan tal cual, en su orden. El spotlight de herramientas (ToolsDiscoveryOverlay) queda diferido, fuera del alcance de MF-13: es desktop-only en web y se retoma cuando exista su versión web móvil o Fabrizio lo pida. La versión ya está al fondo de Más desde MF-03.

Más completo + Ayuda solo se agrega la sección · nada se quita ni reordena
Más · el final de la lista Ayuda, Preferencias, Cerrar sesión y versión, tal cual
Novedades la lista del dropdown, en una hoja

El punto azul de «Novedades» es el bg-blue-500 crudo de la web (deuda anotada): aparece cuando lastSeenChangelogId (MMKV, espejo de mogos.lastSeenChangelogId) no es la última entrada, y abrir la hoja lo apaga (markAsSeen), igual que abrir el dropdown. La hoja no agrega fechas ni cuerpos: la fuente (lib/changelog.ts) solo tiene título por entrada. El discovery queda diferido (decisión 2026-09-24): cuando entre, en una mega-fase posterior, replicará la mecánica web — dos compuertas (onboarding cerrado + flag toolsDiscoveryDismissed), un solo paso — con target y copy nuevos.

06Lo que hay que escribir en Moti

suggestions-widget.css · dialog.tsx · Moti + Reanimated · useReducedMotion
PiezaQué anima (ya existe en web)Duración · easingReduced motion
Halo del FABescala 1 → 1.26 con fundido, en loop, solo con novedades3.2 s · ease-outno se pinta (la insignia queda)
Nacimiento del panelnace del botón: esquina inferior derecha, radio 50 % → 20, opacidad al 60 %640 ms · ease-outaparece resuelta
Chips «Para empezar»suben 8 px con fundido, en cascada420 ms · 50/130/210estado final
Estrella elegidapop escala 0 → 1 — uno de los dos únicos resortes240 ms · springswap directo
Tick de «Lista»el ícono cohete hace pop al llegar el estado — el otro resorte240 ms · springswap directo
Glows de la coronaceleste y lima derivan en alternancia22 s / 31 s · in-outquietos
Composerhueso → blanco + borde celeste-deep + anillo al teclear160 ms · ease-outswap directo
Modal de anuncioespera 500 ms tras montar; velo + card con fade y zoom 95 % (Dialog del sistema)sistema (Dialog)directo, misma espera
Banners (top · flotante)sin animación propia en la web: aparecen resueltos — paridad—igual
Toasts Sileo (página vieja)caen con resorte, como todas las MF anteriores640 ms · springdirecto

«El movimiento es señal, no decoración»: los dos resortes significan algo bueno acaba de pasar. Todo lo demás confirma. Nada de esta tabla es nuevo — es el CSS shippeado del widget más el Dialog del sistema, portado a Moti con useReducedMotion.

07Lo cerrado y lo que decide Fabrizio

docs/specs/2026-09-24-mobile-sugerencias.md · HOLD hasta OK
  • Decidido · Fabrizio 2026-09-24

    Las puertas abren el panel; la página no se porta

    «Enviar sugerencia» (Más) y «Reportar problema» (Crear) abren el panel del widget — el sistema vivo con respuestas de Slack y estados — con el origen estampado en route. El formulario legacy /suggestions no se construye; el rating queda el del widget (opcional, tras el primer mensaje). Sección 03.

  • Decidido · Fabrizio 2026-09-24

    FLOATING_BOTTOM = tarjeta inline en el Inicio

    En móvil el banner no flota: es una tarjeta debajo del header del Inicio que scrollea con el contenido — nunca sobre la tab bar ni el FAB. Tinte soft del type con el acento fuerte en el borde izquierdo, título en bold, texto, CTA como link y X arriba a la derecha. Solo en Inicio, una a la vez (la más reciente); cerrarla la despide para ese usuario. Pares soft/deep de la marca, AA verificado (tabla en 04).

  • Decidido · Fabrizio 2026-09-24

    Ayuda en Más + novedades en hoja: aprobado

    Tal cual la sección 05: «Contactar soporte» (mailto) y «Novedades» (hoja con el changelog + versión), con markAsSeen al abrir. Fuera por paridad: búsqueda deshabilitada, atajos, «Ver todo →», WhatsApp.

  • Decidido · Fabrizio 2026-09-24

    Discovery: diferido, fuera de MF-13

    El ToolsDiscoveryOverlay es desktop-only en web; su versión móvil no entra en esta mega-fase. Se retoma cuando la web tenga una o Fabrizio la pida; la mecánica (dos compuertas, un paso) queda inventariada en 05.

  • Cerrada · paridad

    El panel es pantalla completa

    A 390 la web lo hace inset: 0: «no una hoja a medias». La app lo presenta sobre el shell con la corona bajo el status bar. La vista inicial la decide resolveInitialView (0 → nueva, 1 → hilo, 2+ → historial) y no cambia con los refetch.

  • Cerrada · paridad

    La verdad es el código, no la proposal vieja

    El widget shippeado derivó del diseño aprobado en agosto: hoy tiene la lista «Historial» («Tú:», «Nueva sugerencia»), el saludo largo de lanzamiento (hasta el 1 oct 2026), «No se envió» + «Reintentar» y el composer de respuesta. La maqueta sigue al código; el copy es el de es.json literal.

  • Cerrada · paridad

    Estados por reacciones de Slack, REJECTED invisible

    👀 → «Vista por el equipo» · ✅ → «Anotada para hacerla» · 🚀 → «Lista · ya está en la app» (el único estado con color) · 🗑️ → REJECTED, que el API filtra y el cliente jamás ve. Las respuestas del equipo llegan del hilo de Slack con authorName. Realtime: canal broadcast suggestions-user-{id} → invalidación; en background se reconcilia al volver (riesgo del brief, cubierto con refetch en AppState).

  • Cerrada · paridad

    Anuncios: type pinta, placement coloca

    Cinco types → cinco colores de marca AA (tabla en 04, con su par soft para la tarjeta del Inicio); cuatro placements → cuatro superficies. El modal y mantenimiento no usan el mapa. Un anuncio por superficie; mantenimiento suprime el resto y no se cierra. Escalera de fallback: embed → card con miniatura (bannerImageUrl ?? og-preview, caché 6 h) → «Ver en {Proveedor}». Sin polling: staleTime 60 s + focus/reconnect.

  • Cerrada · paridad

    Rating: una vez, reteniendo el envío

    Solo tras el primer mensaje de la vida y una vez por sesión: el mensaje queda en «Enviando…» hasta elegir o saltar, espera 1200 ms por si cambias, y viaja en el mismo POST /suggestions. En la página vieja el rating es obligatorio; en el widget, opcional.

  • Cerrada · adaptación declarada

    localStorage → MMKV, con scope de usuario donde importa

    mogos.announcements.dismissed.{userId} (visto de anuncios — por usuario, decisión 2026-09-24: en web es por navegador y eterno), mogos.suggestions.openedAt.{userId} (corte de no-leídos) y mogos.lastSeenChangelogId (novedades). La llave del discovery queda para cuando entre. Mismo shape que la web, sin expiración.

  • Cerrada · MF-12

    Tutoriales siguen sin puerta

    La web los quitó de la ayuda (26bf55375) y del ToolsGroup; MF-12 los construyó ocultos tras deep link. MF-13 no abre la puerta: ni fila en Ayuda ni mención en el discovery. Cuando la web la reponga, la fila vuelve aquí.

  • Cerrada · reuso

    Lo que ya existe

    Shell y Más (MF-03), StackHeader (MF-04), hojas y toasts Sileo (MF-01), PressScale, patrón WebView (MF-07), MMKV (MF-00). Es nuevo: SuggestionsFab, SuggestionsPanel (corona, composer, hilo, historial, rating), AnnouncementProvider nativo con top banner, modal, AnnouncementCard del Inicio y mantenimiento, y la sección Ayuda de Más.

  • Deuda anotada

    Lo que la web debe y la app no repite… o replica y anota

    El provider nunca pide targetAudience: CLIENT (TODO web — se replica para no divergir y queda anotado). Copy de anuncios hardcodeado sin i18n (la app lo lleva a claves nuevas). Dots bg-blue-500 fuera de tokens (se replican). «Ver todo →» muerto (se omite). El floating web sin safe-area no se hereda: en móvil es la tarjeta del Inicio. .sug-clearance definido y sin uso. Dismiss de anuncios eterno y sin scope de usuario. og-preview público con allowlist.

  • Testing

    DoD en iPhone = Fabrizio

    Enviar una sugerencia desde el widget → 👀 en Slack → «Vista por el equipo» sin refrescar. Publicar un anuncio con un reel desde el admin y verlo en banner y modal (embeds IG/TikTok en WebView: probar primero). Ver la tarjeta en el Inicio, cerrarla y que no vuelva. Activar mantenimiento y que la app quede bloqueada. Abrir Novedades desde Más. C9 automática: expo export --platform web + visual-qa a 390×844 contra estas 15 maquetas (DECISIONS #4/#24).