diff --git a/Estructura.md b/Estructura.md
new file mode 100644
index 0000000..bb482c5
--- /dev/null
+++ b/Estructura.md
@@ -0,0 +1,131 @@
+# Cómo está organizado el código
+
+> ⚠ Rama experimental, no oficial. El Oasis oficial es
+> https://github.com/epsylon/oasis · [Volver a la portada](Home)
+
+Esta rama tiene que hacer dos cosas a la vez: añadir funciones y **seguir el ritmo de
+epsylon**, que publica una versión cada pocos días. Lo segundo solo sale bien si el
+código propio se distingue del suyo y no le estorba.
+
+Esta página explica dónde vive cada cosa y por qué ahí.
+
+---
+
+## La regla
+
+**Upstream no usa ni un solo subdirectorio** en `src/views`, `src/backend`,
+`src/models`, `src/client/assets/styles` ni `src/client/assets/translations`: 66 vistas
+y 67 modelos, todos planos. La rama sigue esa misma forma. Nada de carpetas propias.
+
+Y los nombres, los suyos: `*_model.js` para modelos, `*_view.js` para vistas,
+`*_views.js` cuando son varias.
+
+## Los diez ficheros propios
+
+| Fichero | Qué es | A qué se parece en upstream |
+|---|---|---|
+| `src/models/karvan_model.js` | Las salas efímeras y su ciclo de vida | `polls_model.js` |
+| `src/views/karvan_view.js` | La lista de salas y la sala | `chats_view.js` |
+| `src/backend/karvan_routes.js` | Las rutas del módulo | — (ver abajo) |
+| `src/client/public/js/karvan.js` | El cliente WebRTC del navegador | `pdf-viewer.js` |
+| `src/client/assets/styles/karvan.css` | Los estilos del módulo | `highlight.css` |
+| `src/views/hive_views.js` | El panal, la topbar y la barra inferior | `main_views.js` |
+| `src/views/identities_view.js` | El selector de identidades | cualquier `*_view.js` |
+| `src/backend/accounts.js` | Encontrar y elegir identidades | `nameCache.js` |
+| `src/backend/turnCredentials.js` | Credenciales efímeras para TURN | `wallet_addresses.js` |
+| `src/client/assets/translations/i18n_fork.js` | Los textos de la rama | junto a `i18n.js` |
+
+## Y lo que se toca de lo suyo
+
+**+247 líneas repartidas en 21 ficheros** — una media de once líneas por fichero. Casi
+todo son enganches de una línea:
+
+```js
+// main_views.js: la interfaz de móvil se llama, no se escribe aquí
+const forkNav = require('./hive_views')({ i18n });
+```
+
+La excepción es `OasisMobile.css`, con unas mil líneas: es el tema de la rama, y un
+tema es un fichero entero por definición.
+
+## Las tres decisiones que no son obvias
+
+### Por qué las rutas van aparte
+
+Upstream mete **625 rutas** en `backend.js`, en una sola cadena. Ese fichero se
+reescribe en cada versión: `+833/-286` líneas en la 0.9.2, `+266/-31` en la 0.9.1.
+
+Una ruta añadida en medio de esa cadena entra en conflicto **cada vez**. Por eso las
+del módulo viven en `karvan_routes.js`, montadas en un router propio que se inserta
+antes del de upstream: lo que no casa cae a `next()` y sigue su curso.
+
+Es la única pieza que no imita una existente, y por un motivo medible.
+
+### Por qué la interfaz de móvil está en un fichero suelto
+
+`hive_views.js` no se limita a agrupar: **existe para que `main_views.js` no crezca**.
+Ese fichero cambia mucho entre versiones, y tener el panal dentro obligaría a rehacerlo
+en cada integración.
+
+Sus tres funciones empiezan igual:
+
+```js
+if (process.env.OASIS_MOBILE !== '1') return "";
+```
+
+Así el mismo `main_views.js` sirve para las dos ramas: en escritorio no dibujan nada.
+Por eso [OASIS_LINUX](https://gitea.laenre.net/hacklab/OASIS_LINUX) puede llevarlo
+idéntico sin heredar la interfaz de móvil.
+
+### Por qué las traducciones son un overlay
+
+Upstream tiene once ficheros `oasis_XX.js` y **los reescribe enteros** en cada versión.
+Añadir claves dentro significaría perderlas cada vez.
+
+`i18n_fork.js` las lleva aparte y `i18n.js` las mezcla con un enganche de tres líneas:
+
+```js
+try {
+ const overlay = require('./i18n_fork.js');
+ languages.forEach(l => Object.assign(i18n[l] = i18n[l] || {}, overlay[l] || overlay.en || {}));
+} catch (e) {}
+```
+
+Dentro va **solo lo de la rama**. Los nombres de las categorías, por ejemplo, no están:
+son los de epsylon y se leen de los suyos.
+
+---
+
+## Lo que se ha quitado por sobrar
+
+Revisando esto salieron cosas que no deberían haber estado:
+
+| Qué | Por qué sobraba |
+|---|---|
+| `views/fork/` y `translations/fork/` | Subdirectorios propios; upstream no tiene ninguno |
+| Una segunda ruta `/legacy/export` | Copia exacta de la del dev, en otro fichero |
+| `media-favorites.js` y su JSON | De una versión anterior; nadie lo requería |
+| El campo `subtopic` en el foro | Resto de unos subtemas propios que se descartaron |
+
+Los subtemas de verdad son los de upstream, con sus rutas y sus vistas.
+
+---
+
+## Cómo comprobarlo
+
+Contra un clon de upstream de la misma versión:
+
+```sh
+# ficheros propios de la rama
+for d in src/backend src/models src/views; do
+ diff <(ls ../oasis/$d) <(ls $d) | grep '^>'
+done
+
+# subdirectorios que upstream no tiene (no debería salir ninguno)
+find src/views src/backend src/models -maxdepth 1 -type d | tail -n +2
+
+# líneas añadidas sobre cada fichero suyo
+for f in $(git ls-files 'src/**/*.js'); do
+ [ -f ../oasis/$f ] && n=$(diff ../oasis/$f $f | grep -c '^>') && [ $n -gt 0 ] && echo "$n $f"
+done | sort -rn
+```
diff --git a/Home.md b/Home.md
index e986315..98c436b 100644
--- a/Home.md
+++ b/Home.md
@@ -34,6 +34,8 @@ Pixel 9 (Android 16). No hay ningún montaje: es lo que se ve al usarla.
| **[Módulos](Modulos)** | Encender y apagar partes de la aplicación |
| **[Identidades](Identidades)** | Varias cuentas en el mismo móvil, y el aviso que evita perder una |
| **[Compilar la APK](Compilar)** | Cómo se construye, de dónde sale cada pieza |
+| **[Estructura del código](Estructura)** | Dónde vive cada fichero y por qué ahí |
+| **[Si algo va mal](Problemas)** | Los fallos que han pasado de verdad, y su arreglo |
| **[Estado y hoja de ruta](Roadmap)** | Qué funciona, qué no, y qué está bloqueado |
| **[Qué NO tiene](Limitaciones)** | Lo que falta respecto al oficial, sin adornos |
diff --git a/Interfaz.md b/Interfaz.md
index e4c3b63..e3f29a2 100644
--- a/Interfaz.md
+++ b/Interfaz.md
@@ -165,7 +165,20 @@ tres piezas no se dibujan.
---
-## 8. Los nombres son los de epsylon
+## 8. En horizontal
+
+
+
+El panal se reparte a lo ancho y la barra de abajo se estira. Girar el móvil **no
+recarga la página** ni pierde lo que estuvieras escribiendo: la aplicación se queda
+donde estaba.
+
+Eso hubo que arreglarlo —antes girar dejaba un error de conexión en pantalla—, y está
+contado en [Si algo va mal](Problemas).
+
+---
+
+## 9. Los nombres son los de epsylon
@@ -188,24 +201,17 @@ cae al inglés.
## Cómo está hecho
-Todo lo de esta página vive en **un solo fichero nuevo**, `src/views/fork/hive_nav.js`,
-que exporta las tres funciones de dibujo y la lista de módulos que se pueden anclar.
+Todo lo de esta página vive en **un solo fichero**, `src/views/hive_views.js`, que
+exporta las tres funciones de dibujo y la lista de módulos que se pueden anclar.
`main_views.js` lo llama en tres sitios.
-Se hizo así a propósito: cuando epsylon publica una versión nueva, `main_views.js`
-cambia mucho, y tener la interfaz de la rama en un fichero aparte evita rehacerla en
-cada actualización.
-
-Las tres funciones empiezan igual:
+Las tres empiezan igual:
```js
if (process.env.OASIS_MOBILE !== '1') return "";
```
-Así el mismo código sirve para las dos ramas: en escritorio no dibujan nada. Por eso
-[OASIS_LINUX](https://gitea.laenre.net/hacklab/OASIS_LINUX) puede llevar el mismo
-`main_views.js` sin heredar la interfaz de móvil.
+Así el mismo código sirve para las dos ramas: en escritorio no dibujan nada.
-Los textos propios de la rama están en `src/client/assets/translations/fork/i18n_fork.js`,
-que se mezcla con el `i18n.js` de upstream mediante un enganche de tres líneas. Igual
-que antes: si epsylon reescribe sus traducciones, las de la rama no se pierden.
+Está desarrollado, con el resto de decisiones, en
+**[Estructura del código](Estructura)**.
diff --git a/Problemas.md b/Problemas.md
new file mode 100644
index 0000000..0d119e4
--- /dev/null
+++ b/Problemas.md
@@ -0,0 +1,133 @@
+# Si algo va mal
+
+> ⚠ Rama experimental, no oficial. El Oasis oficial es
+> https://github.com/epsylon/oasis · [Volver a la portada](Home)
+
+Todo lo de esta página ha pasado de verdad durante el desarrollo. No es una lista de
+casos imaginarios.
+
+---
+
+## Al abrir, se queda en una pantalla con un contador
+
+
+
+**No está colgada, está arrancando.** El primer arranque descomprime el servidor —unos
+230 MB— y levanta SSB. En el emulador tarda 15 segundos; en un móvil lento, más.
+
+La pantalla enseña los segundos que lleva para que se note que sigue viva. Cuando el
+servidor responde, carga la interfaz sola.
+
+Si pasa de un par de minutos, mira el registro:
+
+```sh
+adb logcat -s OASIS-NODE:V
+```
+
+Tiene que llegar a `[Oasis] Backend started on port 3000`.
+
+
+
+## Se ve un error de conexión y no se recupera
+
+No debería pasar ya. Si la página no carga, la aplicación **vuelve a esperar al
+servidor y recarga sola** la última pantalla que sí cargó.
+
+Antes sí ocurría, y por dos motivos que están arreglados:
+
+- **Al girar el móvil.** Faltaba una entrada en la lista de cambios de configuración
+ del manifiesto (`smallestScreenSize`), así que Android recreaba la pantalla y el
+ navegador interno recargaba antes de tiempo.
+- **Cuando Android mataba el servidor** por falta de memoria: se quedaba muerta. Ahora
+ vuelve sola en unos veinte segundos.
+
+## No se puede instalar: falta espacio
+
+```
+INSTALL_FAILED_INSUFFICIENT_STORAGE
+```
+
+La APK son unos 80 MB y el servidor se descomprime dentro de los datos de la
+aplicación: cuenta con **unos 350 MB libres**.
+
+Ojo con una trampa: Android rechaza instalar cuando el disco baja de cierto umbral,
+**aunque el hueco dé de sobra para la APK**. En un emulador se ve así, con 500 MB
+libres y una APK de 80. Si te pasa en un emulador de pruebas:
+
+```sh
+adb shell settings put global sys_storage_threshold_percentage 2
+```
+
+En un móvil de verdad, libera espacio y ya está.
+
+## Instalé una APK nueva pero se comporta como la vieja
+
+Pasa si el paquete del servidor no se rehizo. La APK lleva el backend dentro como un
+zip, y si se compila sin volver a empaquetarlo, la aplicación arranca **con el código
+anterior**.
+
+Usa siempre el script, que lo rehace y lo comprueba:
+
+```sh
+cd android
+./scripts/release.sh
+```
+
+Verifica que la APK lleva el servidor, el motor de Node y los dos permisos de cámara y
+micrófono. Ver [Compilar la APK](Compilar).
+
+## No deja instalar: dice que es una versión anterior
+
+```
+INSTALL_FAILED_VERSION_DOWNGRADE
+```
+
+El número de versión sale del número de commits. Si compilas a mano con `./gradlew`
+sin las variables de entorno, sale **1**, que es menor que la instalada. Compila con
+`./scripts/release.sh`, que lo calcula.
+
+## Al pulsar Llamar no pasa nada
+
+Comprueba, por este orden:
+
+1. **Que concediste los permisos.** Ajustes → Aplicaciones → Oasis → Permisos. Si
+ dijiste *Don't allow*, no se vuelve a preguntar.
+2. **Que la APK los declara.** La oficial no lo hace, y entonces ni siquiera aparecen
+ como opción:
+ ```sh
+ aapt dump permissions oasis.apk | grep -E "CAMERA|RECORD_AUDIO"
+ ```
+3. **Que hay otro participante.** Una llamada con uno solo enciende tu cámara y poco
+ más. Ver [Llamadas](Llamadas).
+
+## Los iconos se ven como cuadros vacíos (□)
+
+En el móvil no debería pasar. En **escritorio** sí, y no es un fallo de Oasis: usa
+caracteres Unicode poco comunes y al sistema le falta una fuente que los cubra.
+
+```sh
+sudo apt install fonts-noto-core # Debian, Ubuntu y derivadas
+```
+
+## Subí una foto y llevaba mi ubicación
+
+Es un problema conocido y **sin resolver**: las imágenes suben con sus metadatos EXIF,
+incluida la posición GPS.
+
+Mientras tanto: quita la ubicación de las fotos antes de subirlas, o desactiva el
+guardado de ubicación en la cámara. Ver [Qué NO tiene](Limitaciones).
+
+## Se cierra al salir de la aplicación
+
+No debería: el servidor corre como servicio en primer plano justamente para seguir
+replicando en segundo plano. Comprobado a los veinte minutos.
+
+Si se corta, mira si la notificación de Oasis sigue en la barra. Si la denegaste, el
+sistema puede matar el servicio antes con la memoria justa. Se vuelve a conceder en
+Ajustes → Aplicaciones → Oasis → Notificaciones.
+
+## Cambié de identidad y sigo viendo la anterior
+
+El cambio **se aplica al reiniciar**, no al momento. En el móvil hay un botón
+**Reiniciar ahora** en la misma pantalla; sin él, cerrar la aplicación no basta porque
+el servidor sigue vivo en segundo plano. Ver [Identidades](Identidades).
diff --git a/_Sidebar.md b/_Sidebar.md
index e2273c8..787e38c 100644
--- a/_Sidebar.md
+++ b/_Sidebar.md
@@ -13,6 +13,11 @@
- [Módulos](Modulos)
- [Identidades](Identidades)
- [Compilar la APK](Compilar)
+- [Estructura del código](Estructura)
+
+---
+
+- [Si algo va mal](Problemas)
---
diff --git a/img/26-espera.png b/img/26-espera.png
new file mode 100644
index 0000000..36cfbe4
Binary files /dev/null and b/img/26-espera.png differ
diff --git a/img/27-horizontal.png b/img/27-horizontal.png
new file mode 100644
index 0000000..5a1dc8c
Binary files /dev/null and b/img/27-horizontal.png differ