/* La fuente Inter ya NO se pide con @import desde aquí.
   Un @import dentro de una hoja de estilos es el peor sitio para una fuente: el
   navegador tiene que descargar y analizar TODO este fichero antes de descubrir
   que además necesita otra hoja de un tercero (fonts.googleapis.com), y hasta
   que esa llega no puede pintar texto. Medido en el arnés, esa cadena era lo
   último en terminar de toda la carga.
   Ahora vive en vendor/inter/inter.css, se anuncia en el <head> con un <link> y
   sus dos woff2 van con <link rel="preload">, así que el preescáner del parser
   las ve en la primera pasada y salen del propio dominio. */

/* ── CSS Variables ────────────────────────────────────────── */
/* The four authored tones and their semantic slots live in index.html so the
   inline theme blocks and this sheet can never drift apart. Everything below
   reads from those tokens; the values here are only fallbacks for the (brief)
   moment before the inline block is parsed. */
body {
  --bg-base:var(--bg);
  --glass-fill:color-mix(in srgb, var(--ink) 5%, transparent);
  --glass-fill-md:color-mix(in srgb, var(--ink) 9%, transparent);
  --glass-fill-hi:color-mix(in srgb, var(--ink) 14%, transparent);
  --glass-border:color-mix(in srgb, var(--ink) 12%, transparent);
  --glass-border-hi:color-mix(in srgb, var(--ink) 20%, transparent);
  --blur:          blur(18px);
  --blur-sm:       blur(10px);

  --violet:var(--violet-deep);
  /* El acento de la app sale de la PALETA DE ORKAT (253237 · 5c6b73 · 9db4c0 ·
     c2dfe3 · e0fbfc). Antes era un violeta que no está en la paleta y se notaba
     sobre todo en la pantalla de entrada. Los cuatro tokens de "violeta" se
     conservan por compatibilidad (los usan muchas reglas), pero ahora valen el
     cian claro de la paleta. El dorado sigue siendo SÓLO para lo premium. */
  --violet-glow:   rgba(194,223,227,0.26);
  --violet-light:var(--violet);
  --cyan:var(--brand);
  --cyan-glow:     var(--brand-glow, rgba(111,194,208,0.30));
  --emerald:var(--success);
  --emerald-glow:  var(--success-glow, rgba(108,199,154,0.30));
  --amber:var(--warn);
  --amber-glow:    var(--warn-glow, rgba(232,180,100,0.30));
  --red:var(--danger);
  --red-glow:      var(--danger-glow, rgba(234,112,118,0.30));

  --text-primary:var(--ink);
  --text-secondary:var(--ink-2);
  --text-muted:var(--ink-3);

  --radius-sm:     8px;
  --radius-md:     14px;
  --radius-lg:     20px;
  --radius-xl:     28px;
}

/* ── Reset & Base ─────────────────────────────────────────── */
*{box-sizing:border-box;}

body {
  font-family:'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', 'Meiryo', 'Malgun Gothic', sans-serif;
  /* El suelo es un color plano a propósito. Aquí había tres radial-gradient
     (marca / éxito / violeta) que daban ambiente a la página, pero con
     #hero a z-index:-1 y color-scheme:dark en <html> el navegador considera
     opaco el fondo raíz, no propaga el fondo de <body> al lienzo y esos
     degradados terminaban pintándose ENCIMA del hero: un velo verde y violeta
     sobre todo el fondo. El ambiente lo da ahora la columna de agua, que sí
     está donde se cree que está. */
  background: var(--bg-base);
  color: var(--text-primary);
  margin:0;
  overflow:hidden;
}

/* ── Scrollbars ───────────────────────────────────────────── */
::-webkit-scrollbar{width:8px;height:8px;}
::-webkit-scrollbar-track{background:transparent;}
::-webkit-scrollbar-thumb{background:var(--line);border-radius:4px;}
::-webkit-scrollbar-thumb:hover{background:var(--brand);}

/* ── Contenedores con scroll: sitio reservado para la barra ──────────────────
   La barra de esta app mide 8px y es de las que OCUPAN sitio (no flotante), así
   que cuando aparece le quita 8px al contenido. Medido: la rejilla de carpetas
   pasa de columnas de 392px a 388px al llenarse, y como hay pantallas que
   desbordan y otras no, al movernos entre secciones TODO el contenido se
   desplaza 8px a un lado. Eso es lo que se veía como módulos y lecciones mal
   posicionados.

   `scrollbar-gutter: stable` reserva ese hueco siempre, haya scroll o no, así
   que el ancho no cambia nunca. El `padding-top` de 4px es para que el anillo y
   el levantamiento de la primera tarjeta al pasar el ratón no queden cortados
   por el borde del área con scroll. */
.orkat-scroll {
  scrollbar-gutter: stable;
  padding-top: 4px;
}

/* ── App Shell ────────────────────────────────────────────────────────────── */
/* `align-self: flex-start` NO es decorativo: es el arreglo del hueco de arriba en
   el móvil, y conviene dejar escrito por qué.

   El <body> lleva `h-screen` (100vh) y las utilidades `flex items-center`, o sea
   que CENTRA verticalmente a sus hijos. El armazón mide `100dvh`. En un escritorio
   esos dos números son iguales y no pasa nada — y por eso no se veía en el arnés—,
   pero en un móvil real NO lo son: `100vh` es el viewport GRANDE (como si la barra
   del navegador estuviera escondida) y `100dvh` el que se ve AHORA. Con la barra a
   la vista, el armazón es más bajo que el body, el centrado lo deja en medio y
   aparece una franja vacía arriba —con el fondo de la app detrás, que es lo que la
   hacía parecer parte del diseño—. Medido en el arnés estirando el body como en un
   teléfono: 28 px de hueco con 56 px de barra, 32 con 64 y 48 con 96.

   Anclado arriba, el armazón ocupa exactamente el área visible y no sobra nada. */
.app-shell{display:flex;height:100vh;height:100dvh;width:100vw;overflow:hidden;align-self:flex-start;}

.sidebar {
  width:72px;
  background: #ffffff08;
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border-right: 1px solid var(--glass-border);
  display:flex;flex-direction:column;align-items:center;
  padding:20px 0;flex-shrink:0;z-index:20;
  transition:transform 0.3s ease,width 0.3s ease,opacity 0.3s ease;
}

.main {
  flex:1;display:flex;flex-direction:column;
  overflow:hidden;background:transparent;position:relative;
}

body.in-lesson .sidebar{transform:translateX(-100%);width:0;opacity:0;padding:0;border:none;}
body.in-lesson .main{width:100vw;}

/* ── Sidebar Items ────────────────────────────────────────── */
.sbi {
  width:48px;height:48px;display:flex;align-items:center;justify-content:center;
  border-radius:var(--radius-sm);color:var(--text-muted);cursor:pointer;
  transition:all .2s;margin-bottom:12px;font-size:24px;border:1px solid transparent;
}
.sbi:hover {
  color:var(--text-primary);
  background: var(--glass-fill-md);
  border-color: var(--glass-border);
}
.sbi.active {
  color:var(--violet-light);
  background: var(--violet-glow);
  border-color: var(--violet-glow);
  box-shadow: 0 0 16px var(--violet-glow);
}
.tab-btn {
  padding:18px 4px 14px;color:var(--text-muted);font-size:15px;font-weight:800;
  letter-spacing:.04em;cursor:pointer;border-bottom:3px solid transparent;
  transition:all .2s;background:none;
  border-top:none;border-left:none;border-right:none;text-transform:uppercase;
}
.tab-btn:hover{color:var(--text-secondary);}
.tab-btn.active{color:var(--violet-light);border-bottom-color:var(--violet-light);}

#reader-text-scroller {
  scrollbar-gutter: stable;
}

/* ── Badges ───────────────────────────────────────────────── */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-size:15px;font-weight:800;padding:12px 24px;border-radius:12px;border:none;cursor:pointer;transition:all .15s;letter-spacing:0.04em;user-select:none;-webkit-user-select:none;}
.badge{display:inline-flex;align-items:center;gap:7px;font-size:13px;font-weight:500;padding:7px 16px;border-radius:20px;border:1px solid;cursor:pointer;transition:filter .15s;user-select:none;}
.badge:hover{filter:brightness(1.25);}

.modal-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100dvh;background:rgba(0,0,0,.75);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);z-index:100;align-items:center;justify-content:center;}
.modal-overlay.open{display:flex;}
.modal-box {
  background: var(--surface);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--glass-border-hi);
  border-radius: var(--radius-lg);
  width:900px;max-width:96vw;max-height:92vh;
  display:flex;flex-direction:column;overflow:hidden;
  box-shadow: 0 32px 80px rgba(0,0,0,0.7), 0 0 0 1px var(--violet-glow);
}

.level-tbl{width:100%;border-collapse:collapse;}
.lvl-row{cursor:pointer;transition:background .1s;}
.lvl-row:hover{background:var(--glass-fill);}
.lvl-row.sel{background:var(--violet-glow);outline:1px dashed var(--violet-glow);outline-offset:-2px;}
.lvl-row td{padding:8px 10px;font-size:12px;color:var(--text-muted);vertical-align:middle;}
.lvl-row td:first-child{color:#6cc79a;font-weight:700;font-size:13px;width:52px;text-align:right;padding-right:14px;}
.lvl-row td:not(:first-child){text-align:center;line-height:1.7;}

/* ── Toggles ──────────────────────────────────────────────── */
.toggle-row{display:flex;align-items:center;gap:10px;cursor:pointer;}
.tog{width:38px;height:21px;background:#ffffff1a;border-radius:12px;position:relative;transition:background .2s;flex-shrink:0;cursor:pointer;}
.tog.on{background:var(--violet);box-shadow:0 0 10px var(--violet-glow);}
.tog::after{content:'';position:absolute;top:2.5px;left:2.5px;width:16px;height:16px;background:#fff;border-radius:50%;transition:transform .2s;box-shadow:0 1px 3px rgba(0,0,0,.3);}
.tog.on::after{transform:translateX(17px);}

/* ── Settings Panel ───────────────────────────────────────── */
.settings-overlay{display:none;position:fixed;top:0;left:0;width:100%;height:100dvh;background:rgba(0,0,0,.4);backdrop-filter:blur(4px);z-index:150;}
.settings-overlay.open{display:block;}
.settings-panel {
  position:fixed;top:0;right:0;bottom:0;width:380px;
  background: var(--surface);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border-left: 1px solid var(--glass-border-hi);
  overflow-y:auto;z-index:151;
  transform:translateX(100%);transition:transform .25s ease;
  /* OJO: la sombra va en `.open`, NO aquí. Cerrado, este panel queda justo fuera de la
     pantalla por la derecha (translateX(100%)) y su sombra (-20px con 60 de difuminado)
     se colaba 80 px DENTRO de la pantalla, oscureciendo todo el lateral derecho de la
     app. Medido con tools/leer-captura.py: en un móvil la mitad derecha salía 13-22
     puntos de brillo más oscura que la izquierda, y se veía como un difuminado negro. */
}
.settings-panel.open{transform:translateX(0);box-shadow: -20px 0 60px rgba(0,0,0,0.6);}
.stog-row{display:flex;align-items:center;gap:10px;padding:11px 0;border-bottom:1px solid #ffffff0d;}
.stog-row:last-of-type{border-bottom:none;}
.stog-swatch{width:13px;height:13px;border-radius:3px;flex-shrink:0;}

.hidden{display:none!important;}
.srow{border-bottom:1px solid #ffffff0d;transition:background .15s;}
.srow:hover{background:#ffffff05;}
@keyframes spin{to{transform:rotate(360deg);}}
@keyframes skel{0%{background-position:200% 0}100%{background-position:-200% 0}}
.skel-bar{background:linear-gradient(90deg,var(--violet-glow) 25%,var(--violet-glow) 50%,var(--violet-glow) 75%);background-size:200% 100%;border-radius:8px;animation:skel 1.6s infinite;}
.lesson-consecutive{text-align:center;font-size:10px;font-weight:800;color:#6cc79a;letter-spacing:.1em;line-height:1;height:12px;visibility:hidden;transition:visibility 0s;}

/* ── Word Chips (practice) ────────────────────────────────── */
.wb-chip {
  padding:8px 14px;border-radius:var(--radius-sm);
  border:1px solid var(--glass-border-hi);
  background:var(--glass-fill);
  color:var(--text-primary);font-size:14px;font-weight:600;
  cursor:pointer;transition:all .15s;line-height:1.3;
}
.wb-chip:hover:not(:disabled){border-color:var(--violet-glow);background:var(--violet-glow);color:#fff;}
.wb-chip-sel{border-color:var(--violet)!important;background:var(--violet-glow)!important;color:var(--violet-light)!important;box-shadow:0 0 10px var(--violet-glow);}
.wb-chip-ph{opacity:0;pointer-events:none;}

/* ── Answer Options ───────────────────────────────────────── */
.answer-opt {
  width:100%;padding:14px 18px;
  background:var(--glass-fill);
  border:1px solid var(--glass-border-hi);
  border-radius:var(--radius-md);
  color:var(--text-secondary);font-size:14px;font-weight:600;
  cursor:pointer;text-align:left;
  transition:border-color .15s,background .15s,color .15s;
  margin-bottom:10px;display:block;line-height:1.4;
}
.answer-opt:hover:not(:disabled){border-color:var(--violet-glow);background:var(--violet-glow);color:#fff;}
.answer-opt:disabled{cursor:default;}
.answer-opt.opt-correct{border-color:var(--success)!important;background:var(--success-glow)!important;color:#6cc79a!important;box-shadow:0 0 12px var(--emerald-glow);}
.answer-opt.opt-wrong{border-color:var(--danger)!important;background:var(--danger-glow)!important;color:#ea7076!important;box-shadow:0 0 12px var(--red-glow);}
.answer-opt.opt-selected{border-color:var(--violet)!important;background:var(--violet-glow)!important;color:#fff!important;}

/* ── Animations ───────────────────────────────────────────── */
@keyframes popIn{0%{transform:scale(.85);opacity:0}100%{transform:scale(1);opacity:1}}
@keyframes tooltipIn{0%{transform:translateX(-50%) scale(.85);opacity:0}100%{transform:translateX(-50%) scale(1);opacity:1}}
.pop-in{animation:popIn .22s ease forwards;}
@keyframes shake{0%,100%{transform:translateX(0)}20%,60%{transform:translateX(-6px)}40%,80%{transform:translateX(6px)}}
.shake{animation:shake .35s ease;}
@keyframes pulse-rec{0%,100%{opacity:1}50%{opacity:.55}}
@keyframes pulse-mic{0%,100%{box-shadow:0 0 0 6px var(--violet-glow)}50%{box-shadow:0 0 0 16px var(--violet-glow)}}
.mic-pulse{animation:pulse-mic 1.4s ease-in-out infinite;}

/* ── Driver.js Tour ───────────────────────────────────────── */
.driver-popover{
  background:var(--surface)!important;
  backdrop-filter:blur(20px)!important;
  border:1px solid var(--glass-border-hi)!important;
  border-radius:var(--radius-md)!important;
  color:var(--text-primary)!important;
  font-family:'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif!important;
  box-shadow:0 16px 48px rgba(0,0,0,.7),0 0 0 1px var(--violet-glow)!important;
  max-width:280px!important;
}
.driver-popover-title{color:#fff!important;font-size:15px!important;font-weight:800!important;margin-bottom:4px!important;}
.driver-popover-description{color:var(--text-secondary)!important;font-size:13px!important;line-height:1.5!important;}
.driver-popover-progress-text{color:var(--text-muted)!important;font-size:12px!important;}
.driver-popover-footer{margin-top:12px!important;gap:8px!important;}
/* Los botones del tour viven dentro de `.driver-popover-navigation-btns`, y driver.js le
   pone su propio fondo y su propio borde: con el estilo de los botones encima, aquello se
   veía como DOS pastillas superpuestas (el usuario lo describió como "duplicado"). Se
   neutraliza el contenedor: manda el estilo de cada botón, nada más. */
.driver-popover-navigation-btns{background:none!important;border:none!important;padding:0!important;box-shadow:none!important;display:flex!important;gap:8px!important;}
.driver-popover-navigation-btns button{border-radius:var(--radius-sm)!important;}
.driver-popover-prev-btn{background:var(--glass-fill-md)!important;border:1px solid var(--glass-border)!important;color:var(--text-secondary)!important;border-radius:var(--radius-sm)!important;font-weight:700!important;padding:8px 14px!important;}
.driver-popover-next-btn{background:var(--violet)!important;border:none!important;color:#fff!important;border-radius:var(--radius-sm)!important;font-weight:800!important;padding:8px 16px!important;box-shadow:0 4px 14px var(--violet-glow)!important;}
.driver-popover-close-btn{color:var(--text-muted)!important;}
/* A PRUEBA DE BALAS: nada de lo que herede del resto de la app (sombras de texto,
   filtros, fondos) puede colarse aquí y hacer que una etiqueta parezca doble. Y si por
   lo que sea quedaran dos popovers (dos tours a la vez), el segundo no se apila: el
   arreglo de verdad está en _makeTour, que cierra el anterior antes de crear el nuevo. */
.driver-popover-footer button{text-shadow:none!important;filter:none!important;white-space:nowrap!important;line-height:1!important;}
.driver-popover-footer button *{text-shadow:none!important;}
.driver-popover-footer,.driver-popover-footer span,.driver-popover-footer button{opacity:1!important;}
.driver-popover-footer button.driver-popover-btn-disabled{opacity:.45!important;}
.driver-popover + .driver-popover{display:none!important;}
.driver-active-element{outline:2px solid var(--violet-light)!important;outline-offset:4px!important;border-radius:var(--radius-md)!important;box-shadow:0 0 20px var(--violet-glow)!important;}
/* EL TOUR NO BLOQUEA LA APP. driver.js pone `pointer-events:none` a TODO mientras está
   abierto (obliga a ir botón a botón), y eso en la pantalla de práctica es molesto: el
   usuario quiere poder seguir tocando las tarjetas. Aquí se devuelve el puntero: el tour
   queda como un cartel que se cierra con la X, y la app sigue viva debajo. Comprobado
   además que así el tour no se come los clics (antes, nada más abrir una lección, el
   propio tour impedía pulsar hasta cerrarlo). */
.driver-active *{pointer-events:auto!important;}
.driver-active .driver-overlay{pointer-events:none!important;}

/* ── Pantalla de entrada ────────────────────────────────────────────────────
   Es una PÁGINA, no un modal. Antes era una tarjeta de cristal flotando en el
   centro con un resplandor violeta detrás, y en el móvil se leía como una
   ventanita encima de la app. Ahora ocupa la pantalla completa, con el fondo de
   la app y el contenido alineado arriba.

   Colores: SÓLO la paleta de Orkat (253237 · 5c6b73 · 9db4c0 · c2dfe3 · e0fbfc).
   El botón principal va en cian claro (#80F2F2) con texto oscuro, que es lo que
   se lee bien sobre el fondo. El dorado queda reservado para lo premium. */
#auth-overlay {
  position:fixed;top:0;left:0;width:100%;height:100dvh;
  background:var(--bg);
  z-index:1000;
  display:flex;align-items:flex-start;justify-content:center;
  padding:0 22px;overflow-y:auto;-webkit-overflow-scrolling:touch;
}
.auth-box {
  width:100%;max-width:400px;
  padding:48px 0 44px;   /* página: sin fondo, sin borde y sin sombra */
}
.auth-marca{display:flex;align-items:center;justify-content:center;gap:9px;margin-bottom:22px;}
.auth-marca i{color:var(--warn);font-size:22px;}
/* La marca es la orca. El PNG ya viene recortado sin fondo (el script está en el
   arnés), así que aquí sólo se le da el tamaño: ni fondo ni borde. */
.auth-marca img{width:46px;height:46px;object-fit:contain;}
.auth-marca span{font-size:18px;font-weight:900;letter-spacing:.01em;color:var(--ink);}
.auth-titulo{font-size:27px;line-height:1.15;font-weight:900;text-align:center;color:var(--ink);margin:0 0 26px;letter-spacing:-.02em;}
.auth-sub{color:var(--ink-3);font-size:13px;text-align:center;margin-bottom:28px;}
.auth-tabs{display:flex;background:var(--glass-fill);border-radius:var(--radius-sm);padding:4px;margin-bottom:22px;gap:4px;}
.auth-tab{flex:1;padding:10px;border:none;background:transparent;color:var(--ink-3);font-size:13px;font-weight:700;cursor:pointer;border-radius:6px;transition:all .2s;}
.auth-tab.active{background:var(--violet-deep);color:var(--bg);}
.auth-input {
  width:100%;
  background:var(--glass-fill);
  border:1px solid var(--glass-border-hi);
  border-radius:var(--radius-sm);
  padding:14px 15px;color:var(--ink);font-size:14px;
  margin-bottom:11px;outline:none;transition:border-color .2s;
  display:block;
}
.auth-input:focus{border-color:var(--violet-deep);box-shadow:0 0 0 3px var(--violet-glow);}
.auth-input::placeholder{color:var(--ink-dim);}
.auth-btn-primary {
  width:100%;padding:15px;border:none;
  border-radius:var(--radius-sm);font-size:15px;font-weight:900;
  cursor:pointer;
  background:var(--violet-deep);
  color:var(--bg);margin-bottom:14px;transition:all .2s;letter-spacing:.01em;
}
.auth-btn-primary:hover:not(:disabled){filter:brightness(1.06);}
.auth-btn-primary:disabled{opacity:.45;cursor:default;}
.auth-btn-google {
  width:100%;padding:14px;
  border:1px solid var(--glass-border-hi);
  border-radius:999px;font-size:16px;font-weight:800;
  cursor:pointer;
  background:var(--ink);
  color:var(--bg);
  display:flex;align-items:center;justify-content:center;gap:11px;transition:filter .2s;
}
/* Con Google como ÚNICA entrada, este botón ya no es "la alternativa al correo":
   es LA acción de la pantalla, así que se ve como un botón principal (píldora
   clara, grande y con el texto oscuro). */
.auth-btn-google:hover{filter:brightness(1.05);}
.auth-btn-google:disabled{opacity:.5;cursor:default;}
/* La letra pequeña de la entrada: la explicación de debajo del botón y la línea
   legal. Las dos en el gris de la paleta, para que no compitan con el botón. */
.auth-nota{margin-top:18px;font-size:12.5px;color:var(--ink-3);text-align:center;line-height:1.6;}
.auth-nota a{color:var(--ink-2);text-decoration:underline;cursor:pointer;}
.auth-divider{display:flex;align-items:center;gap:12px;margin:16px 0;color:var(--ink-dim);font-size:12px;}
.auth-divider::before,.auth-divider::after{content:'';flex:1;height:1px;background:var(--glass-border-hi);}
/* El enlace de "¿Olvidaste tu contraseña?" NO tenía estilo y salía con el azul
   morado del navegador: era otro de los colores que no son de la paleta. */
.auth-forgot{display:block;text-align:right;font-size:12.5px;color:var(--ink-3);cursor:pointer;margin:-2px 0 14px;transition:color .2s;}
.auth-forgot:hover{color:var(--ink);}
.auth-error{color:var(--danger);font-size:12px;text-align:center;margin-top:10px;min-height:16px;line-height:1.4;}
.auth-ok{color:#6cc79a;font-size:12px;text-align:center;margin-top:10px;line-height:1.4;}
.auth-forgot{display:block;text-align:right;color:var(--text-muted);font-size:12px;margin-top:-4px;margin-bottom:14px;cursor:pointer;}
.auth-forgot:hover{color:var(--text-secondary);}

/* ── User Chip / Menu ─────────────────────────────────────── */
#user-chip{width:34px;height:34px;border-radius:50%;background:linear-gradient(135deg,var(--violet),var(--cyan));color:#fff;font-size:12px;font-weight:700;display:flex;align-items:center;justify-content:center;cursor:pointer;position:relative;box-shadow:0 0 12px var(--violet-glow);}
#user-menu {
  position:absolute;left:76px;bottom:60px;
  background:var(--surface);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border:1px solid var(--glass-border-hi);
  border-radius:var(--radius-sm);
  padding:6px;min-width:180px;z-index:50;display:none;
  box-shadow: 0 16px 40px rgba(0,0,0,0.6);
}
#user-menu.open{display:block;}
.user-menu-email{font-size:11px;color:var(--text-muted);padding:6px 10px 8px;border-bottom:1px solid #ffffff0f;margin-bottom:4px;word-break:break-all;}
.user-menu-btn{width:100%;text-align:left;padding:8px 10px;background:none;border:none;color:var(--text-secondary);font-size:13px;cursor:pointer;border-radius:6px;}
.user-menu-btn:hover{background:var(--glass-fill-md);color:#fff;}

.node-circle {
  width:72px;height:72px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  font-size:32px;cursor:pointer;position:relative;
  border:2px solid;
  backdrop-filter: var(--blur-sm);
  -webkit-backdrop-filter: var(--blur-sm);
  transition: all .2s;
}
.node-circle.active {
  background: linear-gradient(135deg,var(--violet-glow),var(--brand-glow));
  border-color: rgba(167,139,250,0.7);
  color:white;
  box-shadow: 0 0 0 6px var(--violet-glow), 0 0 30px var(--violet-glow);
}
.node-circle.completed {
  background: linear-gradient(135deg,var(--warn-glow),var(--success-glow));
  border-color: var(--warn);
  color:var(--amber);
  box-shadow: 0 0 16px var(--amber-glow);
}

.node-tooltip {
  position:absolute;top:-60px;left:50%;transform:translateX(-50%);
  background: var(--surface);
  backdrop-filter: var(--blur);
  color:var(--text-primary);
  font-weight:800;font-size:13px;
  padding:10px 18px;border-radius:var(--radius-sm);
  border:1px solid var(--glass-border-hi);
  white-space:nowrap;z-index:10;
  animation:tooltipIn 0.3s cubic-bezier(0.175,0.885,0.32,1.275) forwards;
  box-shadow: 0 8px 24px rgba(0,0,0,0.5);
}
.node-tooltip::after{content:'';position:absolute;bottom:-7px;left:50%;transform:translateX(-50%);border-width:7px 7px 0;border-style:solid;border-color:var(--surface) transparent transparent transparent;}
.node-tooltip::before{content:'';position:absolute;bottom:-9px;left:50%;transform:translateX(-50%);border-width:9px 9px 0;border-style:solid;border-color:var(--violet-glow) transparent transparent transparent;}

/* ── Interlinear / Reader ─────────────────────────────────── */
.interlinear-word{display:inline-flex;flex-direction:column;align-items:center;margin:0px 4px 16px 4px;cursor:pointer;transition:all 0.2s ease;position:relative;}
.interlinear-word .pinyin{font-size:11px;color:var(--violet-light);font-weight:500;margin-bottom:2px;}
.interlinear-word .character{font-size:24px;font-weight:400;color:var(--text-primary);padding:0px 2px 2px 2px;border-bottom:4px solid transparent;transition:all 0.15s ease;}
.interlinear-word:hover .character{background:#ffffff14;border-radius:4px 4px 0 0;}
.interlinear-word.selected .character{background:#ffffff1f;border-radius:4px 4px 0 0;}
.interlinear-word.word-level-1 .character{border-bottom-color:#b3a4dd;}
.interlinear-word.word-level-2 .character{border-bottom-color:#80F2F2;}
.interlinear-word.word-level-3 .character{border-bottom-color:#6fc2d0;}
.interlinear-word.word-level-4 .character{border-bottom-color:#ea7076;}
.interlinear-word.word-known .character{color:#6cc79a;border-bottom-color:transparent;}
.interlinear-word.word-ignore .character{color:#ffffff;border-bottom-color:transparent;}
.interlinear-word.word-ignore .pinyin{color:#ffffff;}
.interlinear-word.word-neutral .character{color:#fff;border-bottom-color:transparent;}
.interlinear-word.word-known .pinyin{color:#6cc79a;}
.interlinear-word.word-studying .character{color:var(--amber);}
.interlinear-word.word-studying .pinyin{color:var(--amber);}
.interlinear-word.word-level-1 .character{border-bottom:3px solid #b3a4dd;}
.interlinear-word.word-level-2 .character{border-bottom:3px solid #80F2F2;}
.interlinear-word.word-level-3 .character{border-bottom:3px solid #6fc2d0;}
.interlinear-word.word-level-4 .character{border-bottom:3px solid #ea7076;}
.reader-word{display:inline;padding:2px 5px;margin:0 1px;border-radius:4px;background:var(--violet-glow);color:var(--text-primary);cursor:pointer;font-size:22px;font-weight:400;line-height:1.9;transition:background .1s;}
.reader-punct{display:inline;color:var(--text-secondary);font-size:22px;line-height:1.9;}
.reader-para-break{display:block;height:14px;}
.reader-sent-line{display:flex;align-items:flex-start;gap:8px;border-radius:6px;padding:2px 4px;margin:2px -4px;}
.reader-play-btn{color:#6cc79a;background:none;border:none;cursor:pointer;font-size:22px;margin-top:2px;opacity:0.6;transition:all 0.15s;flex-shrink:0;}
.reader-sent-line:hover .reader-play-btn{opacity:1;}
.reader-play-btn:hover{color:#6cc79a;transform:scale(1.15);}
.reader-play-btn.playing{color:#80F2F2;opacity:1;transform:scale(1.15);}
.reader-word:hover{background:var(--brand-glow);}
.reader-word.selected{background:var(--brand-glow);outline:1.5px solid var(--cyan);}
.reader-word.word-known{background:var(--success-glow);color:#6cc79a;}
.reader-word.word-studying{background:var(--warn-glow);color:var(--amber);}
.reader-word.word-ignore{background:#ffffff26;color:#ffffff;}
.reader-word.word-neutral{background:#ffffff0d;color:var(--text-muted);}
.reader-word.word-level-1{border-bottom:3px solid #b3a4dd;}
.reader-word.word-level-2{border-bottom:3px solid #6fc2d0;}
.reader-word.word-level-3{border-bottom:3px solid #8fd39a;}
.reader-word.word-level-4{border-bottom:3px solid #ea7076;}

/* ── Buttons ──────────────────────────────────────────────── */
.btn-duo{border-bottom-width:4px;transition:filter 0.15s,transform 0.1s,border-bottom-width 0.1s;font-weight:800;letter-spacing:0.05em;border-radius:var(--radius-md);}
.btn-duo:active{border-bottom-width:0px;transform:translateY(4px);margin-bottom:4px;}
.btn-duo-blue{background:linear-gradient(135deg,var(--cyan),#4aa3b4);border-color:#4aa3b4;color:white;box-shadow:0 4px 16px var(--cyan-glow);}
.btn-duo-gray{background:var(--glass-fill-md);border:1px solid var(--glass-border-hi);border-bottom-width:4px;color:var(--text-secondary);}

@media (hover: hover) {
  .btn-duo-blue:hover{filter:brightness(1.1);}
  .btn-duo-gray:hover{background:var(--glass-fill-hi);}
}
.btn-dict-small{}
.btn-dict-small{flex:1;height:40px;border-radius:var(--radius-sm);font-weight:900;font-size:14px;display:flex;align-items:center;justify-content:center;background:var(--glass-fill);border:none;border-bottom:3px solid var(--glass-border-hi);transition:all 0.15s;cursor:pointer;opacity:0.4;filter:saturate(0.4);outline:none;}
.btn-dict-small:hover{opacity:0.8;filter:saturate(0.8);}
.btn-dict-small:active{border-bottom-width:0px;transform:translateY(3px);margin-bottom:3px;}
.bs-active{background:var(--glass-fill-hi);border-color:currentColor;opacity:1!important;filter:saturate(1)!important;}

Practice Transition Card ─────────────────────────────── */
@keyframes modalPop{0%{opacity:0;transform:scale(0.9);}100%{opacity:1;transform:scale(1);}}
@keyframes ptFadeIn{0%{opacity:0}100%{opacity:1}}
@keyframes ptSlideUp{0%{opacity:0;transform:translateY(28px)}100%{opacity:1;transform:translateY(0)}}
@keyframes ptIconPop{0%{opacity:0;transform:scale(.4)}60%{transform:scale(1.15)}100%{opacity:1;transform:scale(1)}}
@keyframes ptGlow{0%,100%{box-shadow:0 0 0 0 transparent}50%{box-shadow:0 0 40px 10px var(--violet-glow)}}
@keyframes ptStep{0%{opacity:0;transform:translateX(-12px)}100%{opacity:1;transform:translateX(0)}}

.pt-overlay{position:fixed;top:0;left:0;width:100%;height:100dvh;z-index:2000;background:rgba(0,0,0,0);display:flex;align-items:center;justify-content:center;animation:ptFadeIn .35s ease forwards;animation-fill-mode:both}
.pt-overlay::before{content:'';position:absolute;inset:0;background:var(--bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);}
.pt-card {
  position:relative;
  background: #ffffff0f;
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border:1px solid var(--glass-border-hi);
  border-radius:var(--radius-xl);
  padding:36px 32px 28px;
  max-width:380px;width:calc(100%-40px);text-align:center;
  animation:ptSlideUp .4s cubic-bezier(.22,1,.36,1) .1s both;
  box-shadow:0 32px 80px rgba(0,0,0,.7), 0 0 0 1px var(--violet-glow);
}
.pt-icon{width:72px;height:72px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:30px;margin:0 auto 16px;animation:ptIconPop .5s cubic-bezier(.22,1,.36,1) .2s both,ptGlow 2.4s ease 0.7s infinite;}
.pt-label{font-size:10px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;margin:0 0 6px;opacity:.5;}
.pt-title{font-size:20px;font-weight:900;color:#fff;margin:0 0 20px;letter-spacing:.02em;}
.pt-steps{text-align:left;display:flex;flex-direction:column;gap:10px;margin-bottom:24px;}
.pt-step{display:flex;align-items:flex-start;gap:12px;opacity:0;animation:ptStep .4s ease both;}
.pt-step-num{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;flex-shrink:0;margin-top:1px;}
.pt-step-text{font-size:13px;color:var(--text-secondary);line-height:1.45;font-weight:600;}
.pt-step-text strong{color:#fff;}
.pt-btn{width:100%;padding:14px;border-radius:var(--radius-md);border:none;font-weight:800;font-size:15px;cursor:pointer;letter-spacing:.04em;opacity:0;animation:ptSlideUp .35s ease both;}

/* Animation for loading dots */
.loading-dots {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 1.5em;
  line-height: 1;
}
.loading-dots span {
  animation: loadingDots 1.4s infinite both;
  color: var(--violet-light);
}
.loading-dots span:nth-child(2) {
  animation-delay: .2s;
}
.loading-dots span:nth-child(3) {
  animation-delay: .4s;
}

@keyframes loadingDots {
  0% { opacity: .2; transform: translateY(0); }
  20% { opacity: 1; transform: translateY(-3px); }
  40%, 100% { opacity: .2; transform: translateY(0); }
}

/* ── Mobile Bottom Nav ────────────────────────────────────── */
.mobile-bottom-nav {
  display: none;
}

@media (max-width: 640px) {
  /* Hide desktop sidebar */
  .sidebar { display: none !important; }

  /* Main content fills full width + leaves room for bottom nav */
  .main { padding-bottom: 60px; }

  /* Show mobile bottom nav */
  .mobile-bottom-nav {
    display: flex;
    position: fixed;
    bottom: 0; left: 0; right: 0;
    height: 56px;
    background: var(--surface);
    backdrop-filter: var(--blur);
    -webkit-backdrop-filter: var(--blur);
    border-top: 1px solid var(--glass-border);
    z-index: 100;
    align-items: center;
    justify-content: space-around;
    padding: 0 8px;
  }

  .mob-nav-item {
    flex: 1;
    display: flex; align-items: center; justify-content: center;
    height: 100%;
    font-size: 22px;
    color: var(--text-muted);
    cursor: pointer;
    transition: color 0.15s;
  }
  .mob-nav-item:active { color: #fff; }
  .mob-nav-item.mob-nav-active { color: var(--violet-light); }
  .mob-nav-flag {
    width: 32px; height: 24px;
    object-fit: cover; border-radius: 4px;
    border: 1.5px solid var(--glass-border);
  }

  /* User menu: repositioned above bottom nav on mobile */
  #user-menu {
    position: fixed !important;
    bottom: 60px !important; left: auto !important; right: 12px !important;
  }

  /* In reader: hide bottom nav and reclaim its space */
  body.in-lesson .mobile-bottom-nav { display: none !important; }
  body.in-lesson .main { padding-bottom: 0 !important; }

  /* Reader nav bar: fixed at bottom so Chrome's dynamic address bar never covers it */
  body.in-lesson #reader-nav-bar {
    position: fixed !important;
    bottom: 0; left: 0; right: 0;
    z-index: 200;
    background: #2b3438;
    border-top: 1px solid #43535a;
    padding: 8px 12px !important;
    display: none;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
  }
  body.in-lesson #reader-nav-bar button {
    padding: 10px 16px !important;
    font-size: 14px !important;
  }

  /* Text scroller: only needs to clear the nav bar (60px) */
  body.in-lesson #reader-text-scroller {
    padding-bottom: 60px;
  }

  /* Dict panel: bottom sheet that slides up over the text when a word is tapped */
  body.in-lesson #reader-dict-panel {
    position: fixed !important;
    bottom: 52px;          /* sits just above the nav bar */
    left: 0; right: 0;
    height: 0 !important;
    max-height: none !important;
    overflow: hidden !important;
    border-radius: 1.5rem 1.5rem 0 0 !important;
    margin: 0 !important;
    z-index: 150;
    flex-shrink: unset !important;
    transition: height 0.25s ease;
  }
  body.in-lesson #reader-dict-panel.dict-expanded {
    height: 300px !important;
    overflow: hidden !important;
  }

}

/* --- Glassmorphism Bottom Sheet (Mobile) & Floating Side Panel (Desktop) --- */
#reader-dict-panel {}

/* --- Glassmorphism Bottom Sheet (Mobile) & Floating Side Panel (Desktop) --- */
#reader-dict-panel {
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s ease;
}

@media (max-width: 767px) {
  #reader-dict-panel {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    transform: translateY(110%);
    opacity: 0;
    pointer-events: none;
    z-index: 50;
  }
  #reader-dict-panel.dict-expanded {
    transform: translateY(0);
    opacity: 1;
    pointer-events: auto;
  }
}

/* Flashcards UX */
.fc-tab {}

/* Flashcards UX */
.fc-tab { background:transparent; border:none; outline:none !important; color:#80F2F2; font-size:14px; font-weight:700; padding:6px 14px; border-radius:8px; cursor:pointer; transition:all 0.2s; }
.fc-tab:focus { outline:none !important; border:none !important; }
.fc-tab:hover { color:#fff; background:#ffffff14; }
.fc-tab.active { background:var(--glass-fill-hi); color:#fff; box-shadow:0 2px 8px rgba(0,0,0,0.2); }

/* Estilos unificados para Flashcard Word.
   La palabra se queda naranja; del Nivel 1 al 4 solo cambia la rayita inferior.
   Solo pasa a verde en "Conocida" y a blanco en "Descartada". */
.flashcard-word { display: inline-block !important; max-width: 100% !important; word-break: break-word !important; overflow-wrap: anywhere !important; white-space: pre-wrap !important; padding: 4px 10px !important; border-radius: 8px !important; font-weight: 700 !important; color: #80F2F2; }
.flashcard-word.word-studying { color: #80F2F2 !important; }
.flashcard-word.word-known { color: #6cc79a !important; }
.flashcard-word.word-ignore { color: #ffffff !important; }
.flashcard-word.word-level-1 { border-bottom: 4px solid #b3a4dd !important; }
.flashcard-word.word-level-2 { border-bottom: 4px solid #6fc2d0 !important; }
.flashcard-word.word-level-3 { border-bottom: 4px solid #8fd39a !important; }
.flashcard-word.word-level-4 { border-bottom: 4px solid #ea7076 !important; }

@keyframes timer-pulse-orange { 0%, 100% { box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.4); } 50% { box-shadow: 0 0 0 10px rgba(245, 158, 11, 0); } }
.timer-pulse-orange { animation: timer-pulse-orange 2s infinite; }

/* Carrusel de Tarjetas */
.fc-carousel-inner { position: relative; width: 100%; height: 100%; min-height: 250px; text-align: center; transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1); transform-style: preserve-3d; cursor: pointer; }
.fc-carousel-card.flipped .fc-carousel-inner { transform: rotateX(180deg); }
.fc-carousel-face { position: absolute; top: 0; left: 0; width: 100%; height: 100%; backface-visibility: hidden; -webkit-backface-visibility: hidden; display: flex; flex-direction: column; justify-content: center; align-items: center; background: #37454b; border: 1px solid #ffffff14; border-radius: 24px; padding: 40px; box-shadow: 0 10px 40px rgba(0,0,0,0.5); overflow-y: auto; }
.fc-carousel-back { transform: rotateX(180deg); background: #2b3438; }
@media (max-width: 640px) { body.fc-hide-search #fc-search-container { display: none !important; } }
.no-scrollbar::-webkit-scrollbar {}
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }

/* ── Tarjeta de oración que se está reproduciendo ────────────────────────────
   El borde por sí solo no bastaba: con 300 tarjetas, "no se sabe en qué tarjeta
   está" era literal, y en cuanto el resaltado se perdía no había forma de
   recuperarlo. La activa lleva ahora tres señales a la vez —borde y halo, barra
   lateral encendida y un altavoz en su cabecera— más el "vas por la 12 de 300"
   de la cabecera de la lección (#practice-now), que no depende del scroll. */
.practice-card.active-card {
  position: relative;
  border-color: #6fc2d0 !important;
  background: #37454b !important;
  box-shadow: 0 0 0 2px var(--brand-glow), 0 8px 24px var(--brand-glow) !important;
}
/* La barrita cian del borde izquierdo de la tarjeta se quitó a petición del usuario:
   solo quería el CONTORNO brilloso (el borde y el halo de .active-card), sin la raya
   lateral. */
.practice-card.active-card .top-text { color: #fff; }
.practice-card.active-card .card-num { color: #6fc2d0 !important; }
/* NADA de animación aquí a propósito. Se probaron dos versiones más caras y las
   dos se retiran con la medida en la mano (tools/perf.mjs):
     · un <span> con el altavoz en cada tarjeta: 600 nodos más y el fotograma p50
       del scroll pasaba de 16,7 ms a 33,3 ms — el doble;
     · un latido (animation infinita) sobre la tarjeta activa: un elemento
       animando sin parada obliga al compositor a trabajar en cada fotograma.
   Lo que queda identifica la tarjeta igual de bien y no cuesta por frame: borde
   y halo, barra lateral encendida, el número en color de marca y el "vas por la
   3 de 40" de la cabecera, que no depende del scroll. */
/* El tamaño de letra de la lección, ADEMÁS del que elija el usuario. En
   escritorio la tarjeta es mucho más ancha (900 px frente a 600) y el mismo texto
   se quedaba pequeño dentro: sube un 15 % sólo en pantallas grandes. Va con un
   número fijo dentro del media query y no con una variable que se multiplique a
   sí misma, porque `--pc-s: calc(1.15 * var(--pc-s))` es un ciclo y el navegador
   descarta la propiedad entera (el usuario perdería su tamaño elegido). */
@media (min-width: 1024px) {
  [class*="text-size-"] .top-text { font-size: calc(1.25rem * var(--pc-s, 1) * 1.15) !important; }
  [class*="text-size-"] .bottom-text,
  [class*="text-size-"] .translit-line { font-size: calc(0.9375rem * var(--pc-s, 1) * 1.15) !important; }
  [class*="text-size-"] .practice-card .card-num { font-size: calc(1rem * var(--pc-s, 1)) !important; }
}

/* La fila de los siete botones de nivel en escritorio: con la tarjeta ancha
   sobran 4 px de separación y se agradecen. */
@media (min-width: 1024px) {
  #practice-card-view .practice-levels { gap: 8px !important; }
}

/* ── Escritorio: contenedores más anchos ─────────────────────────────────────
   Toda la app estaba medida para un móvil (columnas de 600-800 px) también en
   pantallas grandes, así que en un monitor se veía una franja estrecha en el
   centro. Aquí se ensanchan SÓLO a partir de 1024 px: el móvil no cambia nada y
   en escritorio se aprovecha la pantalla sin inventar una estructura nueva. */
@media (min-width: 1024px) {
  .orkat-scroll { padding-top: 6px; }
}

/* Botones de nivel de la tarjeta (X, libro, 2-5, ✓): mismo aspecto y rayita que
   los de Cuentos (.btn-dict-small / .bs-active), pero de 30px y sin "#". */
/* Botones de nivel (X, 1, 2, 3, 4, 5, ✓), con el semáforo que pidió el usuario.
   SIN elegir: el número va en SU color (--plv, que lo pone el HTML) sobre el fondo
   apagado, para que se vea el semáforo entero de un vistazo.
   ELEGIDO: no se rellena a tope, se marca con un fondo tenue —como la X— PERO en su
   propio color, para que no pierda el sitio que ocupa en el semáforo (antes se quedaba
   gris y no se sabía cuál estaba marcado).
   Valen tanto para las tarjetas de la lección como para los que salen en el ejercicio. */
/* Botones de nivel, como estaban antes del intento al estilo Spotify: los que NO están
   elegidos se apagan bastante y el elegido se enciende, con un fondo tenue de su color.
   El fondo del botón no se colorea a tope. */
.practice-card .plv,
.p-niveles .plv{line-height:1;width:30px;height:30px;flex:0 0 auto;border-radius:9px;display:flex;align-items:center;justify-content:center;background:var(--glass-fill);border:none;border-bottom:3px solid var(--plv,var(--glass-border-hi));color:var(--plv,#0388A6);font-size:12px;font-weight:800;opacity:.4;filter:saturate(.55) brightness(.85);transition:all .15s;cursor:pointer;outline:none;}
.practice-card .plv:hover,
.p-niveles .plv:hover{opacity:.9;filter:saturate(1);}
.practice-card .plv.active,
.p-niveles .plv.active{opacity:1;filter:saturate(1.35) brightness(1.15);background:color-mix(in srgb, var(--plv) 22%, transparent);color:var(--plv);border-bottom-color:var(--plv);border-bottom-width:4px;box-shadow:none;font-weight:900;}
/* La fila de botones dentro del ejercicio. Los botones son EXACTAMENTE los mismos que
   los de las tarjetas (mismos selectores, sin reglas propias), pero aquí detrás tienen
   el fondo oscuro del pie y se perdían: se les pone la MISMA superficie que tiene una
   tarjeta (#37454b) para que se lean igual que en la lección. */
.p-niveles{display:flex;justify-content:center;gap:8px;margin-bottom:10px}
/* Flechas de la tarjeta: sin selección de texto ni menú contextual, para poder dejar
   el dedo apoyado y que avance sola. */
.pc-flecha{touch-action:manipulation;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;}

/* ── Selector de modo de práctica: Exprésate / Recuerda ─────────────
   Los rótulos cambiaron, pero los nombres internos NO: el modo sigue siendo
   'sombra' / 'recuerdo' en localStorage y en las clases de estado de abajo,
   porque es un ajuste que la gente ya tiene guardado.
   Antes el JS reemplazaba TODA la lista de clases de los botones y se
   perdían el padding (px-3/px-4) y el py-1.5, por eso el botón activo
   se montaba encima del texto del otro botón.

   OJO AL EDITAR ESTE COMENTARIO: estaba partido en dos, con el primero cerrado
   antes de tiempo y el resto del texto suelto justo aquí encima. Como el parser
   de CSS se traga como "preludio" todo lo que hay entre el final del bloque
   anterior y la siguiente llave, aquel texto se llevaba por delante la regla
   `.mode-switch` COMPLETA: el contenedor se quedaba sin display:flex. Un
   comentario tiene que abrir y cerrar UNA vez. */
.mode-switch {
  display: flex; align-items: center; gap: 2px;
  padding: 3px; border-radius: 9999px;
  background: #37454b; border: 1px solid #ffffff0f;
  flex: 0 0 auto;
}
.mode-pill {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 6px; padding: 6px 12px; border: 0; border-radius: 9999px;
  background: transparent; color: #888;
  font-weight: 700; font-size: 12px; line-height: 1.1;
  white-space: nowrap; cursor: pointer; flex: 0 0 auto;
  -webkit-tap-highlight-color: transparent;
  transition: background-color .18s ease, color .18s ease;
}
.mode-pill i { font-size: 0.95em; }
.mode-pill:hover { color: #fff; }
.mode-pill.is-sombra { background: var(--brand-glow); color: #6fc2d0; }
/* El botón del modo Bloques, encendido mientras está reproduciendo por bloques */
.bloques-btn.is-on { background: var(--brand-glow); color: #6fc2d0; }
/* El botón "Acuérdate" iba en morado (#8f7cc4) y sobre el fondo oscuro casi no se
   veía. Ahora usa el azul claro de la paleta de la app, que sí se lee, manteniendo
   un fondo distinto al de "Exprésate" para que se note cuál está activo. */
.mode-pill.is-recuerdo { background: rgba(194, 223, 227, .13); color: #80F2F2; }
@media (min-width: 640px) {
  .mode-pill { padding: 6px 16px; font-size: 14px; gap: 8px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   ORKAT HERO — el mundo vivo

   Lo que había aquí era un musgo procedural en WebGL: ~190 000 briznas
   instanciadas en escritorio y ~70 000 en móvil, más helechos, flores, polen
   y espray, con shaders propios, tone mapping ACES y blending aditivo. Ese
   coste es de relleno de píxeles (fill-rate), no de descarga, así que se
   pagaba en cada frame y detrás de toda la app: en un portátil con gráfica
   integrada producía esperas de más de 2 s y en un móvil directamente
   congelaba la interfaz, que es justo el síntoma de "hay que pulsar dos
   veces" (el hilo principal no llega al evento).

   El hero cuenta ahora la misma escena que aquel musgo —el suelo vivo de
   Sylva— con lo que la GPU compone prácticamente gratis. De aquel original
   vuelven la PALETA y la LUZ, no la geometría: el musgo de 190 000 briznas es
   justo la parte que congelaba el móvil y no vuelve. El polen sí vuelve, y
   vuelve en WebGL (hero-polen.js), que es donde estaba: son dos nubes de
   puntos aditivos, no un campo de briznas, y su coste es el relleno de unas
   decenas de píxeles por partícula.

     .hero-water    el suelo y su charco de luz. Un degradado: se rasteriza
                    una vez.
     #hero::after   el haz que sube desde el pie, donde el suelo se enciende.
     #hero-plankton las dos nubes de polen: la ambiental y la del puntero.

   Los haces y los brillos (.hero-shafts, .hero-glints) se fueron con este
   cambio: eran la luz de la superficie del mar, y el fondo del original no
   los tiene. Lo que queda es la composición de allí —base plana, charco de
   luz abajo a la izquierda, esquina en sombra arriba a la derecha y haz al
   pie—, que es la que hace que el fondo se lea como un claro de bosque y no
   como un degradado de relleno.

   Ninguna de las dos capas de fondo repinta su contenido por frame, así que
   lo que cuesta por frame es el polen y nada más. Los tres tokens --hero-* se
   redefinen por tema y las mezclas se componen en el punto de uso.
   ══════════════════════════════════════════════════════════════════════════ */
#hero {
  position: fixed; inset: 0; z-index: -1; pointer-events: none; overflow: hidden;
  /* Aísla el pintado del hero del resto del documento: ningún cambio dentro
     puede invalidar layout fuera, ni al revés. */
  contain: layout paint style;
}

.hero-water,
#hero-plankton { position: absolute; }

/* El suelo: base plana y un charco de luz CENTRADO abajo.

   Antes eran dos capas asimétricas: el charco abajo a la IZQUIERDA (27% 84%) y la
   esquina de arriba a la DERECHA en sombra. Esa asimetría era la firma del original,
   pero el usuario la vio como un manchón oscuro en el lateral derecho, y MEDIDO con
   tools/leer-captura.py resultó ser real: en un móvil (576x1280) la mitad derecha
   salía 13-22 puntos de brillo más oscura que la izquierda, porque en pantalla
   estrecha la sombra de la esquina ocupa media pantalla. En escritorio se notaba menos
   (-2 a -6), pero estaba igual.

   Ahora el fondo es limpio y simétrico: la base y un charco de luz centrado y
   ensanchado. Las mezclas se escriben aquí, en el punto de uso, y no como token
   derivado: una variable personalizada se sustituye en el elemento donde se declara, de
   modo que un --hero-mid definido en :root se resolvería con los valores del tema oscuro
   y el tema claro heredaría esa banda oscura. */
.hero-water {
  inset: 0;
  background:
    radial-gradient(120% 90% at 50% 88%,
      color-mix(in srgb, var(--hero-beam) 8.5%, transparent) 0%, transparent 78%),
    var(--hero-base);
}

/* El haz del pie. Va en el ::after de #hero y no dentro de .hero-water para
   poder pintarse por encima del degradado sin tocar su orden de pintado: el
   canvas del polen lleva z-index 1 y queda por encima de los dos, que es como
   estaba en el original (la escena delante del haz). */
#hero::after {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(72% 44% at 50% 117%,
      color-mix(in srgb, var(--hero-beam) 50%, transparent) 0%,
      color-mix(in srgb, var(--hero-beam) 21%, transparent) 42%,
      color-mix(in srgb, var(--hero-beam) 4%, transparent) 72%,
      transparent 88%),
    linear-gradient(180deg,
      transparent 54%,
      color-mix(in srgb, var(--hero-beam) 3%, transparent) 78%,
      color-mix(in srgb, var(--hero-beam) 8.5%, transparent) 100%);
}

#hero-plankton {
  inset: 0; width: 100%; height: 100%; display: block; z-index: 1;
  /* Las motas son manchas suaves: no hay nada que ganar rasterizándolas a 3x
     y sí mucho que perder. El canvas se dibuja a 1x en móvil y el navegador
     lo escala. */
  opacity: .85;
}

/* ── Dentro de la LECCIÓN: sin el haz del pie ─────────────────────────────────

   El fondo ya es liso en TODA la app (ver .hero-water), así que aquí sólo queda
   quitar el haz del pie, que en la lección únicamente añadía un degradado bajo la
   tarjeta. */
body.in-lesson #hero::after { content: none; }

/* Con movimiento reducido el claro se queda quieto y aquí no hay nada que
   apagar: las dos capas de fondo son estáticas. Lo decide el JS —hero-polen.js
   monta la escena, dibuja un solo fotograma y no arranca el bucle, y
   hero-ocean.js tampoco enciende el suyo—, así que no hace falta media query. */

/* El hero es decorativo: si la GPU o el navegador no pueden con él, el suelo
   de la app sigue siendo correcto sin ninguna de estas capas. */
@media (prefers-reduced-transparency: reduce) {
  #hero-plankton { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   KIT DE ACABADO DEL HUB
   Las dos piezas que quedan vienen del hero «Sylva — Living Green» de ThreeUI
   (_legacy/inner-green-3d.html) y son las que no cuestan GPU por frame: la
   entrada por recorte escalonado y el borde especular. El fondo de musgo de
   esa página se queda donde está —es la parte que congelaba el móvil—, pero
   su paleta y su luz sí se traen: ver ORKAT HERO, arriba.

   Aquí vivía además el botón de metal líquido, con su placa, su lienzo y su
   respaldo sin WebGL2. Se va con el botón «Empezar a practicar», que ya no
   existe: el hub entra por las cuatro tarjetas. liquid-metal.js (38 KB) sale
   de la ruta de carga y queda en _legacy/, como three.min.js.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Entrada por recorte escalonado ───────────────────────────────────────
   El clip lo pone el JS al armar la entrada (`ok-reveal-armed`), no el CSS por
   defecto: si el script no llega a correr, el hub se ve en vez de quedarse
   recortado para siempre. Cuando la entrada termina el clip se quita entero —
   un clip vivo abre contexto de apilamiento y atraparía las tarjetas. */
.ok-reveal-armed [data-reveal] {
  clip-path: inset(100% 0 0 0 round var(--okr, 0px));
}
.ok-reveal-armed.ok-reveal-live:not(.ok-reveal-done) [data-reveal] {
  clip-path: inset(0 0 0 0 round var(--okr, 0px));
  transition: clip-path 1.05s var(--ease) var(--d, 0ms);
}
.ok-reveal-done [data-reveal] {
  clip-path: none !important;
  transition: none !important;
}

/* ── Borde especular ─────────────────────────────────────────────────────
   Un conic-gradient enmascarado al borde, cuyo ángulo apunta al puntero y cuya
   opacidad cae con la distancia. Es lo que hace que una caja translúcida lea
   como un canto iluminado. --spec-angle y --spec-bright los escribe ui-chrome.js
   sólo mientras el puntero se mueve. */
[data-spec] {
  position: relative;
  /* Valores de reposo declarados aquí, no sólo como fallback del var(): así el
     borde existe desde el primer pintado (apagado) y validate.mjs ve que los
     tokens están definidos. */
  --spec-angle: 2.4rad;
  --spec-bright: 0;
}
[data-spec]::after {
  content: '';
  position: absolute; inset: -1px; z-index: 5;
  padding: 1px; border-radius: inherit;
  pointer-events: none;
  opacity: var(--spec-bright, 0);
  background: conic-gradient(from var(--spec-angle, 2.4rad) at 50% 50%,
    rgba(224,251,252,0) 0deg, rgba(224,251,252,.08) 14deg, rgba(224,251,252,.95) 28deg,
    rgba(224,251,252,.16) 46deg, rgba(224,251,252,0) 68deg, rgba(224,251,252,0) 180deg,
    rgba(224,251,252,.08) 194deg, rgba(224,251,252,.95) 208deg, rgba(224,251,252,.16) 226deg,
    rgba(224,251,252,0) 248deg, rgba(224,251,252,0) 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
}

/* Con movimiento reducido el borde se queda apagado: el bucle ni arranca. */
@media (prefers-reduced-motion: reduce) {
  [data-spec]::after { opacity: 0 !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   LISTA DE ORACIONES LARGA — no pagar por lo que no se ve

   Una lección puede tener 306 tarjetas y cada una son ~15 nodos (los siete
   botones de nivel incluidos). Montarlas todas y medirlas de golpe bloqueaba el
   hilo principal: es el tirón que se nota al pulsar una tarjeta, y el propio
   navegador lo marcó como "Event handlers on this element blocked UI updates for
   823ms" señalando justo a .sylva-list-item.practice-card.

   `content-visibility: auto` deja que el navegador se SALTE el layout y el
   pintado de las tarjetas que están fuera de pantalla y las calcule cuando se
   acercan. `contain-intrinsic-size` tiene que ir con él: sin un tamaño
   estimado, la barra de scroll no sabe cuánto mide la lista y da saltos. El
   `auto` del valor hace que, una vez medida de verdad una tarjeta, se recuerde
   su altura en lugar de usar la estimación.
   ══════════════════════════════════════════════════════════════════════════ */
#practice-list-container .practice-card {
  content-visibility: auto;
  /* La estimación tiene que parecerse a la tarjeta de verdad. Medida en el
     navegador, una tarjeta de oración mide ~195px (título, traducción,
     transliteración y los siete botones de nivel). Con los 132px que había, la
     barra de scroll calculaba una lista mucho más corta de lo que es y daba
     saltos al bajar: el pulgar cambiaba de tamaño según se iban midiendo las
     tarjetas de verdad. El `auto` sigue recordando la altura real una vez
     medida; esto sólo arregla la estimación de las que aún no se han visto. */
  contain-intrinsic-size: auto 195px;
  /* El `transition-all` que traía obligaba a vigilar todas las propiedades de
     cada tarjeta; aquí sólo cambia el fondo al pasar el ratón. */
  transition: background-color .15s ease;
}
#practice-list-container .practice-card:hover { background-color: #1f2d35; }

/* ══════════════════════════════════════════════════════════════════════════
   IMPORTADOR — estado "te pasas del límite"

   Estas dos clases existen porque el estado rojo se aplicaba con utilidades
   Tailwind de valor arbitrario (`text-[#ef4444]`) desde castillos.js en tiempo
   de ejecución. Eso depende de que el JIT del CDN llegue a ver esa clase, y
   castillos.js ni siquiera pasa por el escáner de tools/validate.mjs (que sólo
   mira app.js), así que el aviso podía quedarse sin color sin que nada
   avisara. En CSS propio el rojo se ve siempre.
   ══════════════════════════════════════════════════════════════════════════ */
.import-limite-num { color: var(--danger) !important; }

/* La caja del importador la mide el JS (_ajustarAltoImportador) para que crezca
   con el texto. Aquí sólo queda el suelo: por debajo de esto no baja nunca, ni
   con la caja vacía. */
#import-textarea { min-height: 180px; overflow-y: hidden; }

.import-limite-btn {
  background: color-mix(in srgb, var(--danger) 20%, transparent) !important;
  color: var(--danger) !important;
  cursor: not-allowed;
}

/* ── Ordenar las colecciones de "Mis castillos" ───────────────────────────────
   El cursor del asa va aquí y no como utilidad Tailwind (`cursor-grab`) porque
   ese nombre no aparece en ningún otro sitio del marcado, y las utilidades que
   sólo existen en el DOM generado dependen de que el JIT del CDN llegue a
   verlas. En CSS propio no hay duda. */
.col-drag-handle { cursor: grab; }
.col-drag-handle:active { cursor: grabbing; }
/* Marca la tarjeta sobre la que se va a soltar. */
.col-drop-target { box-shadow: inset 0 0 0 2px var(--brand); }

/* Asa de las CARPETAS de "Mis castillos": el dedo mueve la tarjeta, no la lista.
   Sin `touch-action: none`, en el móvil el navegador se queda el gesto para hacer
   scroll y la carpeta no se mueve nunca — que es exactamente "no puedo arrastrar
   mis carpetas". */
.carpeta-asa {
  cursor: grab; touch-action: none;
  -webkit-user-select: none; user-select: none;
  display: inline-flex; align-items: center; padding: 2px 4px;
}
.carpeta-asa:active { cursor: grabbing; }
.carpeta-card.carpeta-arrastrando { opacity: .75; box-shadow: 0 14px 34px rgba(0, 0, 0, .5); }

/* ── Configuración: secciones y atajos ────────────────────────────────────────
   En CSS propio y no con utilidades Tailwind porque la clase activa la pone el
   JS en tiempo de ejecución, y una utilidad que el JIT no llega a ver nunca se
   pinta. Mismo motivo que .import-limite-num. */
.settings-tab {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: 12px; border: 0;
  background: transparent; color: #80F2F2;
  font-weight: 700; font-size: 13.5px; text-align: left;
  cursor: pointer; white-space: nowrap; width: 100%;
  transition: background-color .15s ease, color .15s ease;
}
.settings-tab:hover { color: #fff; background: rgba(255, 255, 255, .06); }
.settings-tab.is-active { background: rgba(111, 194, 208, .16); color: #6fc2d0; }
@media (max-width: 640px) {
  /* En móvil la columna se convierte en fila: `width: auto` para que las
     pestañas no se estiren una por línea. */
  .settings-tab { width: auto; flex: 0 0 auto; }
}

/* La cabecera de un tramo (Principiante / Intermedio / Avanzado) tiene que ocupar
   TODA la fila de la rejilla de carpetas, sea de 2 columnas (tablet) o de 3
   (pantallas grandes). Antes era `md:col-span-2`, que con 3 columnas dejaba la
   tercera celda libre: la primera carpeta del tramo se colaba en la fila del
   título y salía arriba a la derecha, más alta y descolocada. */
.grid-tramo { grid-column: 1 / -1; }

/* El número de la tarjeta, en el contador de la vista de una tarjeta, se puede
   escribir para saltar a ella (como el número de página de un visor de PDF).
   Hasta que se pasa por encima o se enfoca se ve igual que el texto de al lado,
   para no llenar la vista de cajas. */
.pc-contador-input {
  background: transparent;
  border: 1px solid transparent;
  border-radius: 7px;
  color: inherit;
  font: inherit;
  text-align: center;
  padding: 1px 2px;
  outline: none;
}
.pc-contador-input:hover { border-color: rgba(255, 255, 255, .16); }
.pc-contador-input:focus { border-color: #6fc2d0; background: rgba(111, 194, 208, .14); }

/* Botones del tono de traducción (Formal / Casual) en Configuración. En CSS
   propio porque la clase activa la pone el JS en tiempo de ejecución y una
   utilidad que el JIT no llega a ver nunca se pinta. */
.tono-btn { background: rgba(255, 255, 255, .05); color: #888; border: 1px solid transparent; }
.tono-btn:hover { color: #fff; background: rgba(255, 255, 255, .1); }
.tono-btn.is-active { background: #6fc2d0; color: #fff; border-color: #6fc2d0; }

/* Fila de un atajo: nombre + tecla. */
.atajo-fila {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: #37454b; border-radius: 12px; padding: 10px 12px;
}
.atajo-tecla {
  min-width: 76px; padding: 7px 12px; border-radius: 10px;
  background: rgba(255, 255, 255, .08); color: #fff;
  border: 1px solid rgba(255, 255, 255, .12);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-weight: 800; font-size: 13px; cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease;
}
.atajo-tecla:hover { background: rgba(255, 255, 255, .14); }
.atajo-tecla.escuchando {
  background: rgba(111, 194, 208, .2); border-color: #6fc2d0; color: #6fc2d0;
}

/* ══════════════════════════════════════════════════════════════════════════
   ANIMACIÓN AL PASAR EL RATÓN — dónde vive de verdad

   Aquí hubo un intento de declarar `.orkat-tile` / `.orkat-row` con su propio
   transform y su propio fondo al hover. Se retiró, y conviene dejar dicho por
   qué: las tarjetas de colecciones, carpetas y lecciones YA llevan la clase
   `.sylva-list-item`, que define en el <style> de index.html su transición y su
   :hover (fondo, borde de marca, halo y levantamiento). Al ser una hoja
   posterior en el documento, con la misma especificidad, ganaba ella — así que
   aquellas dos clases no hacían nada salvo confundir.

   La animación no faltaba: lo que faltaba era `box-shadow` en la lista de
   transición de `.sylva-list-item`, y eso se arregló allí. Un solo sitio.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Tarjetas BLOQUEADAS (desbloqueo progresivo) ────────────────────────────
   Exploración y Fundamentos son un camino: sólo está abierta la primera lección
   de la primera carpeta y lo demás espera su turno. La tarjeta bloqueada SIGUE
   en su sitio, apagada: así el usuario ve cuánto le queda por delante en vez de
   encontrarse una pantalla con una sola tarjeta. El candado y el texto los pone
   castillos.js; aquí sólo se apaga.

   El :hover va con las DOS clases a propósito: `.sylva-list-item:hover` está en
   el <style> de index.html y enciende fondo, halo y levantamiento. Sin ganarle,
   una tarjeta bloqueada se levantaría al pasar el ratón —justo lo que dice "esto
   se puede pulsar"—. */
.orkat-bloqueada{
  filter: grayscale(.85);
  opacity: .45;
  cursor: not-allowed;
}
.sylva-list-item.orkat-bloqueada:hover{
  background: var(--surface);
  border-color: var(--line-soft);
  box-shadow: var(--shadow-soft);
  transform: none;
}


/* ── La fila de arriba de la lección: los interruptores, a la altura del modo ─
   El botón del modo ("Exprésate" / "Traduce") vive dentro de su propia caja
   (`.mode-switch`: la pastilla + 3 px de relleno + 1 px de borde), así que su
   pastilla blanca —que es lo que se ve como botón— queda unos píxeles por debajo
   del borde de arriba de la fila. Los dos interruptores, en cambio, empiezan
   justo en ese borde: tocaban la línea de progreso mientras el botón del modo no,
   y parecía que solo ellos se metían en la barra.

   Se bajan esos mismos píxeles, que son la MITAD de la diferencia de alturas
   entre un interruptor y la pastilla, porque la pastilla va centrada en su caja:

       móvil       (36 - 28) / 2 = 4 px
       desde 640   (40 - 28) / 2 = 6 px

   Y el `padding-bottom` devuelve el hueco de abajo para que la fila de la lupa y
   el engranaje no se muevan. Lo comprueba tools/checks/botones-leccion.js
   midiendo los dos bordes, no a ojo. */
.practice-row-modos{ --bajar-interruptores: 4px; padding-bottom: var(--bajar-interruptores); }
.practice-row-modos .fila-interruptores{ position: relative; top: var(--bajar-interruptores); }
@media (min-width: 640px) { .practice-row-modos{ --bajar-interruptores: 6px; } }

/* ── Vista PRACTICAR (el ejercicio) ─────────────────────────────────────────
   Es una VISTA a pantalla completa dentro de la app, no un modal ni una ventana
   emergente. El fondo va TRANSPARENTE a propósito: así a los lados se ve el mismo
   fondo y las mismas partículas que en la lección (lo pidió el usuario; antes se
   veía todo negro). */
.practicar-capa{position:fixed;top:0;left:0;width:100%;height:100dvh;z-index:80;background:transparent;display:flex;justify-content:center}
.practicar-capa.hidden{display:none}

/* El panel de la LECCIÓN, con la misma receta que el del ejercicio (era lo que le
   faltaba: se medía "blur: ninguno" y un gris plano al 26%, y por eso se notaba tanto
   el rectángulo). Con el desenfoque, el fondo y las partículas se ven suaves por
   detrás, como en la vista de practicar que le gustó al usuario. */
#view-practice-list{
  background:rgba(43,52,56,.4) !important;
  -webkit-backdrop-filter:blur(18px);
  backdrop-filter:blur(18px);
}
/* La caja del ejercicio usa EL MISMO panel que la vista de la lección
   (bg-[#2b3438]/40 + blur + bordes laterales): así se ve igual que las demás vistas
   y a los lados se siguen viendo el fondo y las partículas. */
.practicar-caja{width:100%;max-width:640px;display:flex;flex-direction:column;height:100%;background:rgba(43,52,56,.4);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);border-left:1px solid var(--glass-border);border-right:1px solid var(--glass-border)}
.practicar-cab{display:flex;align-items:center;gap:10px;padding:12px 14px;border-bottom:1px solid #ffffff12}
.practicar-volver{width:38px;height:38px;border-radius:12px;border:none;background:#ffffff12;color:var(--ink);cursor:pointer;flex-shrink:0}
.practicar-titulos{flex:1;min-width:0}
.practicar-titulo{font-weight:800;font-size:15px;color:var(--ink)}
.practicar-sub{font-size:12px;font-weight:600;color:#888;margin-top:2px}
.practicar-dificultad{display:flex;gap:4px}
.practicar-dificultad button{background:transparent;border:1px solid #43535a;color:#80F2F2;font:inherit;font-size:12px;font-weight:800;padding:5px 9px;border-radius:9px;cursor:pointer}
.practicar-dificultad button.on{background:#6fc2d0;border-color:#6fc2d0;color:#0396A6}
/* La línea de progreso de la lección, con el MISMO diseño que la del ejercicio
   (practicar.html, `.barra`): redondeada, y el trozo andado también, para que se
   lea como una línea que avanza y no como un rectángulo pegado al borde. El
   COLOR no se toca: cian de marca sobre el gris de siempre, y sin corazones ni
   nada más. Los 16 px de margen son los mismos que el `px-4` de la cabecera y
   de los botones, así que la línea queda alineada con ellos. */
.practicar-barra{height:6px;margin:0 16px;border-radius:99px;background:#ffffff14;overflow:hidden;flex-shrink:0}
.practicar-barra i{display:block;height:100%;width:0;background:#6fc2d0;border-radius:99px;transition:width .25s}
.practicar-zona{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:14px}
.practicar-pie{padding:12px 16px 18px;border-top:1px solid #ffffff12}
.practicar-seguir{width:100%;height:50px;border-radius:14px;border:none;background:#22c55e;color:#0b2b16;font:inherit;font-weight:900;font-size:16px;cursor:pointer}
.practicar-seguir[hidden]{display:none}
.practicar-seguir.neutro{background:#6fc2d0;color:#0396A6}
.p-enunciado{color:#888;font-size:12px;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.p-foco{background:#37454b;border:1px solid #43535a;border-radius:18px;padding:16px;display:flex;align-items:center;gap:14px}
/* El botón de escuchar del ejercicio es la MISMA píldora gris que el "Rep Nx" de la
   lección (a petición del usuario), pero SIN texto: solo el icono. Antes era un
   círculo de 50 px. El alto y el redondeo van con los del "Rep Nx"
   (#btn-practice-play) para que las dos pantallas se vean igual. */
.p-play{
  height:40px; min-width:58px; padding:0 16px;
  border-radius:9999px; border:1px solid rgba(255,255,255,.10);
  background:rgba(255,255,255,.08); color:#ffffff;
  font-size:16px; cursor:pointer; flex-shrink:0;
  display:inline-flex; align-items:center; justify-content:center;
  transition:background-color .15s ease;
}
.p-play:hover{ background:rgba(255,255,255,.16); }
.p-pista{font-size:15px;font-weight:800;color:var(--ink)}
.p-pista small{display:block;color:#888;font-weight:600;font-size:12px;margin-top:3px}
.p-frase{font-size:25px;font-weight:800;line-height:1.25;color:#ffffff;word-break:break-word}
.p-aviso{font-weight:800;font-size:14px;display:none;align-items:center;gap:8px}
.p-aviso.bien{color:#22c55e;display:flex}
.p-aviso.mal{color:#ef4444;display:flex}
.p-opciones{display:flex;flex-direction:column;gap:10px}
/* Acierto y fallo se pintan en las LETRAS, no en el fondo (lo pidió el usuario): la
   frase elegida se pone roja y sigue probando; cuando acierta, verde. */
.p-opcion{width:100%;text-align:left;background:#37454b;border:1.5px solid #43535a;color:#ffffff;font:inherit;font-size:17px;font-weight:700;padding:15px;border-radius:14px;cursor:pointer;transition:color .15s,border-color .15s}
.p-opcion:hover{border-color:#6fc2d0}
.p-opcion.bien{border-color:#22c55e;color:#22c55e}
.p-opcion.mal{border-color:#ef4444;color:#ef4444}
/* El botón de escuchar: SOLO el icono (el usuario pidió quitar el texto). Se vuelve
   pausa mientras suena, con el fondo oscuro de la lección, sin dorado ni naranja. */
.p-play.pausa{background:#ffffff;color:#0396A6;border-color:#ffffff}
.p-stop{width:38px;height:38px;border-radius:10px;border:none;background:#ef4444;color:#fff;font-size:13px;cursor:pointer;flex-shrink:0}
.p-stop[hidden]{display:none}
/* El botón de Practicar va relleno de color, como pedía el usuario. */
.practicar-pill{background:#6fc2d0 !important;color:#0396A6 !important;font-weight:900}
.practicar-pill:hover{filter:brightness(1.08)}
/* El ejercicio respeta el TAMAÑO DE TEXTO que el usuario eligió en Configuración de
   práctica: la clase text-size-* pone la variable --pc-s (0.8 a 1.4) y aquí se usa
   para escalar el texto y los botones, igual que hace la tarjeta de la lección. */
#view-practicar[class*="text-size-"] .p-enunciado{font-size:calc(12px * var(--pc-s,1))}
#view-practicar[class*="text-size-"] .p-frase{font-size:calc(25px * var(--pc-s,1))}
#view-practicar[class*="text-size-"] .p-pista{font-size:calc(15px * var(--pc-s,1))}
#view-practicar[class*="text-size-"] .p-pista small{font-size:calc(12px * var(--pc-s,1))}
#view-practicar[class*="text-size-"] .p-opcion{font-size:calc(17px * var(--pc-s,1));padding:calc(15px * var(--pc-s,1))}
#view-practicar[class*="text-size-"] .p-foco{padding:calc(16px * var(--pc-s,1))}
#view-practicar[class*="text-size-"] .p-play{
  height:calc(40px * var(--pc-s,1)); min-width:calc(58px * var(--pc-s,1));
  padding:0 calc(16px * var(--pc-s,1)); font-size:calc(16px * var(--pc-s,1));
}
#view-practicar[class*="text-size-"] .p-stop{width:calc(38px * var(--pc-s,1));height:calc(38px * var(--pc-s,1))}
/* Los botones de nivel del ejercicio crecen con el mismo ajuste que el texto (y con
   ellos el hueco entre ellos y el botón de Continuar), para que al subir el tamaño no
   quede un texto enorme con botones diminutos. */
#view-practicar[class*="text-size-"] .p-niveles{gap:calc(8px * var(--pc-s,1));padding-top:calc(6px * var(--pc-s,1))}
/* (El tamaño de estos botones lo pone la app, en la regla de "Tamaño de texto" de
   app.js, la MISMA para las tarjetas y para el ejercicio: 36 px que crecen hasta 44.) */
#view-practicar[class*="text-size-"] .practicar-seguir{height:calc(50px * var(--pc-s,1));font-size:calc(16px * var(--pc-s,1))}
#view-practicar[class*="text-size-"] .practicar-cab{padding:calc(12px * var(--pc-s,1)) calc(14px * var(--pc-s,1))}
#view-practicar[class*="text-size-"] .practicar-zona{gap:calc(14px * var(--pc-s,1));padding:calc(16px * var(--pc-s,1))}
.p-final{text-align:center;padding:22px 6px}
.p-final h2{margin:6px 0 4px;font-size:21px;color:var(--ink)}
.p-final p{color:#80F2F2;font-weight:600}

/* ══ Botones con el criterio de Spotify (lo pidió el usuario) ══════════════════
   En Spotify los botones son una pastilla gris oscura con el texto BLANCO, y el que
   está elegido pasa a BLANCO con el texto oscuro. Se aplica a la barra de la lección
   (Rep, lupa, traducir, audífonos), a Exprésate / Acuérdate, a Practicar y a las
   pastillas de Fácil / Medio / Difícil. Se hace por CSS, sin tocar el HTML. */
.mode-pill,
#btn-toggle-translation,
#btn-toggle-dual-audio,
#btn-practice-search,
#btn-rep,
#practice-repeat-btn,
.practicar-dificultad button,
#practicar-dificultad button{
  background:rgba(255,255,255,.08) !important;
  color:#ffffff !important;
  border:1px solid rgba(255,255,255,.10) !important;
  border-radius:12px !important;
}
.mode-pill i,
#btn-toggle-translation i,
#btn-toggle-dual-audio i,
#btn-practice-search i,
#btn-rep i,
#practice-repeat-btn i{ color:#ffffff !important; }
.mode-pill:hover,
#btn-toggle-translation:hover,
#btn-toggle-dual-audio:hover,
#btn-practice-search:hover,
#btn-rep:hover,
#practice-repeat-btn:hover,
.practicar-dificultad button:hover{
  background:rgba(255,255,255,.16) !important;
}
/* El que está elegido: blanco con el texto oscuro, como en Spotify. */
.mode-pill.is-on,
.mode-pill.is-recuerdo,
#btn-toggle-translation.is-on,
#btn-toggle-dual-audio.is-on,
#practice-repeat-btn.is-on,
#btn-rep.is-on,
.practicar-dificultad button.on,
#practicar-dificultad button.on{
  background:#ffffff !important;
  color:#0396A6 !important;
  border-color:#ffffff !important;
}
.mode-pill.is-on i,
.mode-pill.is-recuerdo i,
#btn-toggle-translation.is-on i,
#btn-toggle-dual-audio.is-on i,
#practice-repeat-btn.is-on i,
#btn-rep.is-on i{ color:#0396A6 !important; }
/* "Practicar" es la acción principal: en Spotify eso va con el color de la marca y el
   texto oscuro, así que se queda en el cian de Orkat. */
.practicar-pill{background:#6fc2d0 !important;color:#0396A6 !important;border-color:#6fc2d0 !important}
.practicar-pill i{color:#0396A6 !important}
/* "Continuar": el texto y la flecha en blanco (el fondo verde se queda como está). */
.practicar-seguir{color:#ffffff !important}
.practicar-seguir i{color:#ffffff !important}

/* Los tres interruptores de la barra (traducir, audífonos, lupa) marcan que están
   encendidos con la clase azul de Tailwind bg-[#3b82f6]. En vez de taparla, se traduce
   al criterio de Spotify: encendido = BLANCO con el icono oscuro. Se limita a esos tres
   botones para no tocar ningún otro sitio que use ese azul. */
#btn-toggle-translation[class*="bg-[#3b82f6]"],
#btn-toggle-dual-audio[class*="bg-[#3b82f6]"],
#btn-practice-search[class*="bg-[#3b82f6]"]{
  background:#ffffff !important;
  color:#0396A6 !important;
  border-color:#ffffff !important;
}
#btn-toggle-translation[class*="bg-[#3b82f6]"] i,
#btn-toggle-dual-audio[class*="bg-[#3b82f6]"] i,
#btn-practice-search[class*="bg-[#3b82f6]"] i{ color:#0396A6 !important; }

/* ── Últimos ajustes de botones (pedidos por el usuario) ──────────────────────
   1) El botón de escuchar del ejercicio, con el MISMO aspecto que el "Rep Nx" de la
      lección: píldora gris con el icono blanco (y sin texto). Sonando se pone BLANCO
      con el icono oscuro, que es el criterio de "encendido" que ya usa la app; así se
      distingue de parado sin depender del icono.
   2) El botón de "Rep Nx" de la lección, con el criterio de Spotify: gris con el texto
      blanco, y blanco con el texto oscuro mientras está sonando.
   3) "Practicar" igual que "Exprésate": gris con el texto blanco (antes iba relleno de
      cian; el usuario prefiere que sea como los demás). */
.p-play{background:rgba(255,255,255,.08) !important;color:#ffffff !important;border-color:rgba(255,255,255,.10) !important}
.p-play:hover{background:rgba(255,255,255,.16) !important}
.p-play.pausa{background:#ffffff !important;color:#0396A6 !important;border-color:#ffffff !important}
.p-play.pausa:hover{background:#ffffff !important}
#btn-practice-play{
  background:rgba(255,255,255,.08) !important;
  color:#ffffff !important;
  border:1px solid rgba(255,255,255,.10) !important;
}
#btn-practice-play i{ color:#ffffff !important; }
#btn-practice-play:hover{ background:rgba(255,255,255,.16) !important; }
#btn-practice-play[class*="bg-[#3b82f6]"]{
  background:#ffffff !important;
  color:#0396A6 !important;
  border-color:#ffffff !important;
}
#btn-practice-play[class*="bg-[#3b82f6]"] i{ color:#0396A6 !important; }
.practicar-pill{
  background:rgba(255,255,255,.08) !important;
  color:#ffffff !important;
  border-color:rgba(255,255,255,.10) !important;
}
.practicar-pill i{ color:#ffffff !important; }
.practicar-pill:hover{ background:rgba(255,255,255,.16) !important; filter:none !important; }

/* La "tira gris" de los botones de nivel en la tarjeta: era el fondo gris claro de cada
   botón, que sobre la tarjeta (más clara que el pie del ejercicio) se notaba mucho. Se
   les deja el fondo TRANSPARENTE: solo se ve el número con su color y su rayita, y el
   que está elegido sigue marcándose con su fondo tenue de color. */
.practice-card .plv,
.p-niveles .plv{ background:transparent !important; }
.practice-card .plv:hover,
.p-niveles .plv:hover{ background:rgba(255,255,255,.07) !important; }
.practice-levels{ background:transparent !important; border:0 !important; }

/* El modal de Configuración, con ALTURA FIJA: así no se mueve ni cambia de tamaño al
   pasar de una pestaña a otra (General, Apariencia, Atajos, Progreso), que era lo que
   lo hacía ver poco profesional. Se le da un alto mínimo y las pestañas se quedan
   siempre en el mismo sitio, como en el panel de ajustes de DeepSeek. */
#settings-modal > div{
  min-height:560px !important;
  max-height:88vh !important;
}
@media (max-height:700px){ #settings-modal > div{ min-height:0 !important; } }

/* ── Correcciones de esta tanda ───────────────────────────────────────────────
   1) Los botones de nivel VUELVEN a su estado anterior: se les devuelve el fondo
      (el usuario prefiere como estaban; la "tira gris" que ve no era el fondo).
   2) El modal de Configuración, con ALTURA FIJA de verdad y scroll por dentro: así no
      cambia de tamaño al pasar de una pestaña a otra. Un min-height no bastaba, porque
      la pestaña más alta lo hacía crecer igual. */
.practice-card .plv,
.p-niveles .plv{ background:var(--glass-fill) !important; }
.practice-card .plv:hover,
.p-niveles .plv:hover{ background:rgba(255,255,255,.14) !important; }
.practice-levels{ background:transparent !important; border:0 !important; }

#settings-modal > div{
  height:min(620px, 88vh) !important;
  max-height:88vh !important;
  overflow:hidden !important;
}
#settings-modal section[data-panel]{
  overflow-y:auto !important;
  max-height:100% !important;
}

/* ══════════════════════════════════════════════════════════════════════════
   VISTA "BUSCAR FRASES" (la lupa de la lección)

   Cada resultado es una fila pulsable: el número de la frase a la izquierda y los
   textos a la derecha. Arriba y en grande el del idioma que ESTUDIAS (que es lo
   que normalmente buscas), debajo la transliteración, luego tu idioma y por último
   el original. La parte que coincide va marcada, así se ve de un vistazo por qué
   ha salido cada fila.
   ══════════════════════════════════════════════════════════════════════════ */
.buscar-fila{
  display:flex; gap:12px; align-items:flex-start; width:100%; text-align:left;
  background:#37454b; border:1.5px solid #43535a; border-radius:14px;
  padding:12px 14px; font:inherit; cursor:pointer;
  transition:border-color .15s ease, background-color .15s ease;
}
.buscar-fila:hover{ border-color:#6fc2d0; background:#3b4a51; }
.buscar-num{
  flex-shrink:0; min-width:2.4em; text-align:right; padding-top:3px;
  color:#0388A6; font-weight:800; font-size:12px;
}
.buscar-textos{ flex:1; min-width:0; }
.buscar-meta{ display:block; color:#ffffff; font-weight:800; font-size:17px; line-height:1.25; word-break:break-word; }
.buscar-translit{ display:block; color:#80F2F2; font-weight:600; font-style:italic; font-size:13px; margin-top:2px; word-break:break-word; }
.buscar-nativa{ display:block; color:#80F2F2; font-weight:600; font-size:14px; margin-top:3px; word-break:break-word; }
.buscar-orig{ display:block; color:#0388A6; font-weight:600; font-size:12px; margin-top:3px; word-break:break-word; }
/* Lo que coincide, marcado. El color es el del acento de la app. */
.buscar-marca{ background:rgba(111,194,208,.25); color:#B3F2F2; border-radius:4px; padding:0 2px; }
.buscar-vacio{ color:#888; font-weight:600; font-size:14px; text-align:center; padding:28px 12px; line-height:1.5; }


/* Login & Sales Tweaks */
body.auth-active #sidebar, body.auth-active .bottom-nav { display: none !important; }
#auth-overlay { background: #B3F2F2 !important; }
#premium-screen { background: #B3F2F2 !important; }

body.auth-active #hero-plankton { display: none !important; }


#auth-overlay .auth-titulo { color: #012E40 !important; }
#auth-overlay .auth-btn-google { background: #FFFFFF !important; color: #012E40 !important; border: 1px solid #9db4c0 !important; box-shadow: 0 4px 12px rgba(1,46,64,0.08) !important; }
#auth-overlay .auth-marca span { color: #012E40 !important; }

body.auth-active { overflow: hidden; }
