/* =============================================================
   app.css — Área de miembros · Marca Personal
   Reutiliza tokens de styles.css. Layout: sidebar + main.
   1. Layout shell   2. Sidebar   3. Topbar   4. Views base
   5. Comunidad  6. Calendario  7. Módulos  8. Programación
   9. Perfil  10. Métricas  11. Responsive
   ============================================================= */

/* Tipografía del área de miembros: la serif (Fraunces) se reemplaza por Raleway.
   Solo aquí (app.html carga app.css); el Home no se ve afectado. */
.app-body { --serif: "Raleway", "Helvetica Neue", Arial, sans-serif; --card-pad: 1.3rem; }
/* Color de EVENTO en toda la plataforma (elegido por el cliente, 29-jul-2026): bloques de la
   agenda del día, píldoras del calendario, el mes y el tipo en la lista de eventos, y la etiqueta de
   la ficha del evento.
   Se sacan del coral a propósito: el coral es el color de las ACCIONES de la plataforma y competía
   con los botones dentro de la misma ventana. La línea de "ahora" (.dayv-now) SÍ sigue en coral: es
   un indicador vivo y tiene que destacar POR ENCIMA de los bloques. */
.app-body { --evt: #6E5D9B; --evt-soft: #ECE8F5; --evt-ink: #52447A; }

/* ---------- App shell ---------- */
/* Mismo guard que ya existe en styles.css (Home): red de seguridad contra cualquier desborde
   horizontal de 1-2px (ej. un elemento fixed mal calculado en móvil) que generaría scroll lateral. */
html:has(body.app-body) { overflow-x: clip; }
body.app-body { background: var(--bg); overflow-x: clip; }
.app { min-height: 100vh; }

.app-icon { width: 20px; height: 20px; flex: 0 0 auto; stroke: currentColor; fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.app-icon--sm { width: 16px; height: 16px; }
[hidden] { display: none !important; }

/* =============================================================
   2. SIDEBAR
   ============================================================= */
.sidebar {
  position: fixed; top: 0; left: 0; bottom: 0; width: 264px; z-index: 60;
  background: var(--paper); border-right: 1px solid var(--line-2);
  display: flex; flex-direction: column; padding: 1.4rem 1rem 1rem;
  transition: transform .45s var(--ease-out);
}
.sidebar-brand { display: flex; align-items: center; gap: .7rem; padding: .4rem .6rem 1.3rem; }
.sidebar-brand img { height: 38px; width: auto; }
.sidebar-brand img.brand-mini { display: none; height: 28px; }
.sidebar-brand .badge { margin-left: auto; font-size: .62rem; color: var(--accent-deep); background: var(--accent-soft); padding: .2rem .5rem; border-radius: 100px; font-weight: 600; }

.sidebar-section-label { font-size: .66rem; color: var(--ink-mute); font-weight: 600; padding: .5rem .8rem; margin-top: .4rem; }
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; }
.side-link {
  display: flex; align-items: center; gap: .8rem; padding: .7rem .8rem; border-radius: 12px;
  color: var(--ink-soft); font-size: .92rem; font-weight: 450; position: relative;
  transition: background .25s var(--ease-out), color .25s var(--ease-out);
}
.side-link:hover { background: var(--bg); color: var(--ink); }
.side-link.is-active { background: var(--grad-box); color: var(--bg); }
.side-link.is-active .app-icon { stroke: var(--bg); }
.side-link .count { margin-left: auto; font-size: .72rem; background: var(--accent-soft); color: var(--accent-deep); padding: .1rem .5rem; border-radius: 100px; font-weight: 600; }
.side-link.is-active .count { background: rgba(244,241,234,.18); color: var(--bg); }
.side-link .lock { margin-left: auto; }
.side-link .lock .app-icon { width: 15px; height: 15px; opacity: .6; }

.sidebar-spacer { flex: 1 1 auto; }
.sidebar-foot { border-top: 1px solid var(--line-2); padding-top: .8rem; margin-top: .8rem; }
.side-profile { display: flex; align-items: center; gap: .7rem; padding: .55rem .6rem; border-radius: 12px; transition: background .25s; cursor: pointer; }
.side-profile:hover { background: var(--bg); }
.side-profile.is-active { background: var(--bg); }
.side-avatar { width: 38px; height: 38px; border-radius: 50%; background: var(--accent-soft) center/cover; display: grid; place-items: center; color: var(--accent-deep); font-family: var(--serif); font-style: italic; font-weight: 600; flex: 0 0 auto; }
.side-profile .meta { min-width: 0; }
.side-profile .meta b { font-size: .86rem; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.side-profile .meta span { font-size: .72rem; color: var(--ink-mute); }
.side-home { display: inline-flex; align-items: center; gap: .5rem; font-size: .8rem; color: var(--ink-mute); padding: .6rem .8rem; }
.side-home:hover { color: var(--accent-deep); }

/* =============================================================
   3. TOPBAR + MAIN
   ============================================================= */
.app-main { margin-left: 264px; min-height: 100vh; display: flex; flex-direction: column; }
.topbar {
  /* z-index 70 (> sidebar 60): el topbar es position:sticky y crea un stacking context,
     así sus desplegables (search/notif/user, z80) quedan POR ENCIMA de la sidebar.
     En móvil, el drawer abierto (overlay 75 + sidebar 80) vuelve a cubrir el topbar. */
  position: sticky; top: 0; z-index: 70;
  display: flex; align-items: center; gap: 1rem;
  padding: 1rem clamp(1.2rem, 3vw, 2.4rem);
  background: var(--grad-portada);
  border-bottom: 1px solid rgba(28,26,24,.25);
}
.topbar-burger { display: grid; width: 40px; height: 40px; border-radius: 10px; border: 1px solid rgba(244,241,234,.24); background: rgba(244,241,234,.12); color: var(--bg); place-items: center; transition: border-color .25s, background .25s; }
.topbar-burger:hover { border-color: rgba(244,241,234,.5); background: rgba(244,241,234,.2); }
.topbar-title { min-width: 0; }
.topbar-title h1 { font-family: var(--serif); font-weight: 300; font-size: clamp(1.4rem, 3vw, 1.9rem); letter-spacing: -.02em; line-height: 1; color: var(--bg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.topbar-title p { font-size: .82rem; color: rgba(244,241,234,.72); margin-top: .2rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: .6rem; }
.topbar-search-wrap { position: relative; display: none; flex: 0 1 460px; min-width: 0; }
.topbar-search { display: flex; width: 100%; align-items: center; gap: .55rem; background: rgba(244,241,234,.16); border: 1px solid rgba(244,241,234,.26); border-radius: 100px; padding: .62rem 1.15rem; color: rgba(244,241,234,.85); transition: background .2s, border-color .2s; }
.topbar-search:focus-within { background: rgba(244,241,234,.24); border-color: rgba(244,241,234,.5); }
.topbar-search .app-icon { opacity: .9; flex: 0 0 auto; }
.topbar-search input { border: 0; background: none; outline: none; font-size: .95rem; width: 100%; color: var(--bg); }
.topbar-search input::placeholder { color: rgba(244,241,234,.6); }
.topbar-search input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; display: none; }
.topbar-search-clear { flex: 0 0 auto; color: rgba(244,241,234,.85); font-size: .9rem; line-height: 1; padding: .1rem .15rem; opacity: .85; transition: opacity .2s; }
.topbar-search-clear:hover { opacity: 1; }
/* Panel de resultados del buscador */
.search-panel { position: absolute; top: calc(100% + 8px); right: 0; left: 0; background: var(--paper); border: 1px solid var(--line); border-radius: 16px; box-shadow: 0 26px 60px -22px rgba(28,26,24,.45); padding: .5rem; z-index: 80; max-height: 64vh; overflow-y: auto; }
.search-panel-label { font-size: .66rem; color: var(--ink-mute); font-weight: 600; padding: .4rem .6rem .2rem; }
.search-panel-hint { font-size: .78rem; color: var(--ink-mute); padding: .55rem .7rem; line-height: 1.4; }
.search-item { display: flex; align-items: center; gap: .7rem; width: 100%; padding: .55rem .6rem; border-radius: 10px; text-align: left; transition: background .2s; }
.search-item:hover { background: var(--bg); }
.search-item b { font-size: .92rem; }
.search-item small { display: block; color: var(--ink-mute); font-size: .76rem; }
.search-av { width: 36px; height: 36px; border-radius: 50%; flex: 0 0 auto; }
.search-hl { background: #FBF1C2; color: inherit; border-radius: 3px; padding: 0 .12em; }
.icon-btn { width: 40px; height: 40px; flex: 0 0 40px; border-radius: 50%; border: 1px solid rgba(244,241,234,.24); background: rgba(244,241,234,.12); display: grid; place-items: center; color: var(--bg); position: relative; transition: border-color .25s, background .25s; }
.icon-btn:hover { border-color: rgba(244,241,234,.5); background: rgba(244,241,234,.2); }
/* Badge de notificaciones estilo iPhone (círculo coral con número y aro claro) */
.icon-btn .dot { position: absolute; top: -5px; right: -5px; min-width: 19px; height: 19px; padding: 0 5px; border-radius: 100px;
  background: var(--accent); color: #fff; border: 2px solid var(--paper);
  font-size: .68rem; font-weight: 700; line-height: 1; display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 5px rgba(28,26,24,.35); }
.icon-btn .dot[hidden] { display: none; }
.topbar-avatar { width: 40px; height: 40px; flex: 0 0 40px; aspect-ratio: 1; border-radius: 50%; background: var(--accent-soft) center/cover; display: grid; place-items: center; color: var(--accent-deep); font-family: var(--serif); font-style: italic; font-weight: 600; box-shadow: 0 0 0 1px rgba(244,241,234,.35); overflow: hidden; }

.app-content { padding: clamp(1.3rem, 3vw, 2.4rem); flex: 1 1 auto; }
.view { display: none; animation: viewIn .5s var(--ease-out); }
.view.is-active { display: block; }
@keyframes viewIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

.app-card { background: var(--paper); border: 1px solid var(--line-2); border-radius: var(--radius); }
.section-mini-label { font-size: .7rem; color: var(--ink-mute); font-weight: 600; margin-bottom: 1rem; }

/* =============================================================
   5. COMUNIDAD (Skool feed)
   ============================================================= */
.feed-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.4rem; align-items: start; min-width: 0; }
.composer { padding: 1.1rem var(--card-pad); margin-bottom: 1.1rem; display: flex; gap: .9rem; align-items: center; }
.composer .side-avatar { width: 42px; height: 42px; }
.composer button { flex: 1; text-align: left; background: var(--bg); border: 1px solid var(--line-2); border-radius: 100px; padding: .8rem 1.2rem; color: var(--ink-mute); font-size: .92rem; transition: border-color .25s; }
.composer button:hover { border-color: var(--line); }
/* El botón coral "+" se eliminó el 28-jul-2026 (pulsar el cuadro de texto ya abría el mismo editor);
   sus reglas se van con él para no dejar CSS muerto. */

.feed-filters { display: flex; gap: .5rem; flex-wrap: wrap; margin: 0 0 1.1rem; }
.comm-admin { padding: .4rem var(--card-pad) .6rem; margin-bottom: 1.1rem; }
.prog-intro { padding: 1.2rem var(--card-pad); margin-bottom: 1.2rem; }
.filter-pill { padding: .45rem 1rem; border-radius: 100px; border: 1px solid var(--line); font-size: .82rem; color: var(--ink-soft); background: var(--paper); transition: background-color .25s var(--ease-out), border-color .25s var(--ease-out), color .25s var(--ease-out); }
.filter-pill:hover { border-color: var(--ink-mute); }
.filter-pill.is-active { background: var(--ink); color: var(--bg); border-color: var(--ink); }

/* Cambiador de comunidad (feed) */
.comm-switch { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: 1.1rem; }
/* Cápsulas para elegir qué comunidad se está viendo, dentro del box de comunidades (admin).
   Sin rótulo encima (29-jul-2026): iba «Ver publicaciones de» y se quitó — el nombre de cada
   comunidad en su cápsula ya dice de qué se trata. Se retiró también su CSS (.comm-view-lbl). */
.comm-view-switch { padding-bottom: .8rem; margin-bottom: .6rem; border-bottom: 1px solid var(--line-2); }
.comm-switch--inbox { margin-bottom: 0; }
.comm-switch-pill { padding: .5rem 1.05rem; border-radius: 100px; border: 1px solid var(--line); font-size: .85rem; font-weight: 500; color: var(--ink-soft); background: var(--paper); transition: background-color .25s var(--ease-out), border-color .25s var(--ease-out), color .25s var(--ease-out); }
.comm-switch-pill:hover { border-color: var(--ink-mute); }
/* Seleccionada en NEGRO, igual que los filtros de tipo de publicación (.filter-pill.is-active):
   el coral queda reservado para las acciones (crear comunidad, publicar), no para marcar el estado
   de un selector — así no compiten dos elementos corales en la misma tarjeta (27-jul-2026). */
.comm-switch-pill.is-active { background: var(--ink); color: var(--bg); border-color: var(--ink); }

/* Selector "Publicar en" del compositor */
.composer-comms { display: flex; flex-direction: column; gap: .55rem; }
.comm-check { display: flex; align-items: center; gap: .6rem; font-size: .92rem; color: var(--ink); cursor: pointer; }
/* Selección múltiple con apariencia de círculo que se rellena (sigue siendo checkbox) */
.comm-check input { appearance: none; -webkit-appearance: none; margin: 0; flex: 0 0 auto;
  width: 19px; height: 19px; border: 2px solid var(--line-2); border-radius: 50%; background: var(--paper);
  display: inline-grid; place-content: center; cursor: pointer; transition: border-color .2s, background .2s; }
.comm-check input::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: #fff;
  transform: scale(0); transition: transform .15s var(--ease-out); }
.comm-check input:hover { border-color: var(--accent); }
.comm-check input:checked { background: var(--accent); border-color: var(--accent); }
.comm-check input:checked::before { transform: scale(1); }
.composer-comms-one { display: flex; align-items: center; gap: .5rem; font-size: .92rem; color: var(--ink-soft); background: var(--bg); border: 1px solid var(--line-2); border-radius: 10px; padding: .6rem .8rem; }
.composer-comms-one .app-icon { width: 16px; height: 16px; color: var(--accent); }

.feed-post { padding: 1.2rem 1.3rem; }
.feed-post + .feed-post { margin-top: 1.1rem; }
/* Medios dentro de una publicación */
.fp-video { position: relative; aspect-ratio: 16 / 9; margin-top: .9rem; border-radius: 12px; overflow: hidden; background: var(--ink); }
.fp-video iframe, .fp-video video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; object-fit: contain; background: #000; }
/* Imágenes de una publicación (9-ago-2026): ya no van a lo ancho debajo del texto, sino como una
   miniatura en una columna estrecha a la derecha. El texto manda y la imagen acompaña.
   minmax(0,1fr) y no 1fr: con 1fr la columna del texto se niega a encogerse por debajo de su
   contenido y una palabra larga o un enlace descuadran la tarjeta entera. */
.fp-cols { display: grid; grid-template-columns: minmax(0, 1fr) 148px; gap: 1.1rem; align-items: start; }
.fp-col-txt { min-width: 0; }
.fp-thumbs { margin-top: .9rem; }
.fp-thumb { position: relative; display: block; width: 100%; aspect-ratio: 4 / 5; border-radius: 12px;
  overflow: hidden; border: 1px solid var(--line-2); background: var(--bg); padding: 0; cursor: zoom-in;
  transition: transform .18s var(--ease-out), box-shadow .18s var(--ease-out); }
.fp-thumb:hover { transform: translateY(-2px); box-shadow: 0 10px 22px -12px rgba(28,26,24,.45); }
/* pointer-events:none en la imagen: el clic lo recibe SIEMPRE el botón, así el visor se abre aunque
   el dedo caiga sobre la foto. De paso, arrastrarla al escritorio deja de funcionar. */
.fp-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none;
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.fp-thumb-more { position: absolute; inset: 0; display: grid; place-items: center; color: #fff;
  font-weight: 600; font-size: 1.05rem; background: rgba(28,26,24,.55); }
/* Documentos adjuntos de una publicación (PDF/Word/Excel/…): tarjeta con nombre + descarga. */
.fp-files { display: flex; flex-direction: column; gap: .5rem; margin-top: .9rem; }
.fp-file { display: flex; align-items: center; gap: .6rem; padding: .6rem .8rem; border: 1px solid var(--line); border-radius: 12px; color: var(--ink-soft); text-decoration: none; transition: background .15s, border-color .15s; }
.fp-file:hover { background: var(--accent-soft); border-color: var(--accent); }
.fp-file .app-icon:first-child { color: var(--accent-deep); }
.fp-file .app-icon:last-child { color: var(--ink-mute); }
.fp-file-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .9rem; font-weight: 500; }
.composer-docs { display: flex; flex-wrap: wrap; gap: .5rem; }
/* Adjuntos en el compositor */
.composer-attach { display: flex; flex-wrap: wrap; gap: .5rem; }
.composer-thumbs { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: .6rem; }
.composer-thumb { position: relative; width: 76px; height: 76px; flex: 0 0 auto; border-radius: 10px; background: var(--bg) center/cover no-repeat; border: 1px solid var(--line-2); }
.composer-chip { position: relative; display: inline-flex; align-items: center; gap: .4rem; margin-top: .6rem; padding: .45rem .7rem; background: var(--bg); border: 1px solid var(--line-2); border-radius: 8px; font-size: .82rem; }
.composer-chip .app-icon { width: 15px; height: 15px; }
.composer-thumb button, .composer-chip button { position: absolute; top: -7px; right: -7px; width: 20px; height: 20px; border-radius: 50%; border: 0; background: var(--ink); color: #fff; font-size: .68rem; line-height: 1; cursor: pointer; display: grid; place-items: center; }
.composer-chip button { position: static; margin-left: .2rem; width: 18px; height: 18px; }
/* Selector de co-ejecutores del compositor (1.1.4): chips de personas que se activan al tocarlas. */
.composer-people { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .2rem; }
.cperson { display: inline-flex; align-items: center; gap: .4rem; border: 1px solid var(--line); background: #fff; color: var(--ink-soft); border-radius: 100px; padding: .26rem .65rem .26rem .3rem; font: inherit; font-size: .82rem; cursor: pointer; }
.cperson .cperson-av { width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; background: var(--grad-box); color: #fff; font-weight: 700; font-size: .64rem; }
.cperson.is-on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-deep); font-weight: 600; }
/* Buscador de co-ejecutores: chips elegidos + input + desplegable de coincidencias. */
.collab-chosen { display: flex; flex-wrap: wrap; gap: .4rem; }
.collab-chosen:not(:empty) { margin-bottom: .5rem; }
.collab-x { border: 0; background: transparent; color: inherit; cursor: pointer; font-size: .72rem; line-height: 1; margin-left: .3rem; padding: 0; }
.collab-search-wrap { position: relative; }
.collab-search { width: 100%; border: 1px solid var(--line); border-radius: 10px; padding: .55rem .75rem; font: inherit; font-size: .9rem; background: #fff; }
.collab-drop { position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 30; background: #fff; border: 1px solid var(--line); border-radius: 10px; box-shadow: 0 10px 28px rgba(28, 26, 24, .16); max-height: 240px; overflow-y: auto; padding: .25rem; }
.collab-opt { display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left; border: 0; background: transparent; border-radius: 8px; padding: .4rem .5rem; font: inherit; font-size: .86rem; color: var(--ink-soft); cursor: pointer; }
.collab-opt .cperson-av { width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center; background: var(--grad-box); color: #fff; font-weight: 700; font-size: .66rem; }
.collab-opt:hover { background: var(--accent-soft); color: var(--accent-deep); }
.collab-empty { padding: .5rem .6rem; font-size: .82rem; color: var(--ink-mute); }
/* Hilo de comentarios del coach en la tarea (fase2/52 · 1.1.9) — diseño aprobado en preview. */
.tc { margin-top: 1.1rem; padding-top: 1rem; border-top: 1px dashed var(--line); }
.tc-title { display: flex; align-items: center; gap: .45rem; font-size: .82rem; font-weight: 700; color: var(--ink); margin: 0 0 .1rem; }
.tc-title .app-icon { width: 15px; height: 15px; color: var(--accent-deep); }
.tc-sub { font-size: .76rem; color: var(--ink-mute); margin: 0 0 .8rem; }
.tc-list { display: flex; flex-direction: column; gap: .7rem; margin-bottom: .9rem; }
.tc-item { display: flex; gap: .6rem; }
.tc-av { width: 34px; height: 34px; border-radius: 50%; flex: 0 0 auto; display: grid; place-items: center; color: #fff; font-weight: 700; font-size: .85rem; }
.tc-av.coach { background: linear-gradient(135deg, #5C322B, #EE8B76); }
.tc-av.ceo { background: linear-gradient(135deg, #1C1A18, #A84A3E); }
.tc-bubble { background: #F4F0E8; border-radius: 12px; padding: .55rem .8rem; min-width: 0; flex: 1; }
.tc-name { font-size: .8rem; font-weight: 700; display: flex; align-items: center; gap: .3rem; }
.tc-name .role { font-weight: 600; color: var(--accent-deep); font-size: .68rem; letter-spacing: .03em; }
.tc-del { border: 0; background: transparent; color: var(--ink-mute); cursor: pointer; font-size: .72rem; margin-left: auto; padding: 0 .1rem; }
.tc-text { font-size: .88rem; color: var(--ink-soft); margin: .1rem 0 0; white-space: pre-wrap; }
.tc-time { font-size: .68rem; color: var(--ink-mute); margin-top: .2rem; }
.tc-form { display: flex; gap: .5rem; align-items: flex-end; }
.tc-form textarea { flex: 1; min-width: 0; border: 1px solid var(--line); border-radius: 12px; padding: .55rem .8rem; font: inherit; font-size: .9rem; resize: vertical; min-height: 42px; background: #fff; }
.tc-send { flex: 0 0 auto; border: 0; background: var(--accent); color: #fff; border-radius: 12px; padding: .55rem .9rem; font-weight: 600; font-size: .85rem; cursor: pointer; }
.tc-readonly { font-size: .8rem; color: var(--ink-mute); background: #F6F2EA; border-radius: 10px; padding: .6rem .8rem; }
.tc-empty { font-size: .84rem; color: var(--ink-mute); font-style: italic; }
/* Banner de límite de sesiones agotadas (2.4). */
.sess-limit { display: flex; gap: .55rem; align-items: flex-start; background: #FBF6EE; border: 1px solid var(--line); border-left: 4px solid var(--accent); border-radius: 12px; padding: .7rem .85rem; margin-bottom: .5rem; }
.sess-limit-ico { color: var(--accent-deep); display: inline-flex; margin-top: 1px; }
.sess-limit-ico .app-icon { width: 18px; height: 18px; }
.sess-limit-t { font-size: .88rem; font-weight: 700; color: var(--ink); }
.sess-limit-s { font-size: .82rem; color: var(--ink-soft); margin: .15rem 0 0; }
.sess-limit-cta { margin-top: .55rem; }
.fp-head { display: flex; align-items: flex-start; gap: .8rem; }
.fp-head .side-avatar { width: 44px; height: 44px; }
.fp-del { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%; border: 0; background: transparent; color: var(--ink-mute); display: grid; place-items: center; cursor: pointer; transition: background .2s, color .2s; }
.fp-del:hover { background: rgba(200,60,40,.1); color: #c33; }
/* Editar publicación (solo el autor del equipo). Mismo botón redondo que eliminar, al lado. */
.fp-edit { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%; border: 0; background: transparent; color: var(--ink-mute); display: grid; place-items: center; cursor: pointer; transition: background .2s, color .2s; }
.fp-edit:hover { background: var(--accent-soft); color: var(--accent-deep); }
/* Fijar/desfijar (solo equipo). Mismo tamaño que el botón de eliminar, al lado. Apagado = atenuado;
   fijada = a plena opacidad, para que se vea de un vistazo cuál está fijada. */
.fp-pin { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%; border: 0; background: transparent;
  display: grid; place-items: center; cursor: pointer; line-height: 1; opacity: .38; transition: opacity .2s, background .2s; }
.fp-pin:hover { opacity: 1; background: var(--accent-soft); }
.fp-pin.is-on { opacity: 1; }
/* Bloque de publicaciones fijadas: va ARRIBA del composer. Mismo espaciado que el feed normal. */
.pinned-feed { display: grid; gap: 1.1rem; margin-bottom: 1.1rem; }
.fp-meta b { font-size: .94rem; }
/* "📌 Fijado" va en su PROPIA fila bajo el nombre y en UNA sola línea (nowrap). El emoji interno debe
   quedar inline: si no, la regla `.fp-meta span { display:block }` lo pondría en su propio renglón y
   partiría "📌" y "Fijado" en dos líneas (el bug que se veía). */
.fp-meta .pin { display: block; white-space: nowrap; color: var(--accent-deep); font-size: .7rem; font-weight: 600; }
/* Icono de "Fijar" = emoji 📌 (decisión del cliente 22-jul-2026). En el botón a 15px; en la etiqueta más chico. */
.fp-meta .pin .pin-emoji { display: inline; font-size: 12px; }
.fp-pin-emoji { font-size: 15px; line-height: 1; }
/* "En colaboración con Ana y Luis": fila propia bajo el nombre, acento coral (1.1.4). */
.fp-meta .fp-collab { display: block; white-space: normal; color: var(--accent-deep); font-size: .74rem; font-weight: 600; margin-top: .1rem; }
.fp-meta .fp-collab .app-icon { display: inline-block; width: 13px; height: 13px; vertical-align: -2px; margin-right: 2px; }
.fp-meta span { font-size: .78rem; color: var(--ink-mute); display: block; }
.fp-cat { margin-left: auto; font-size: .68rem; color: var(--accent-deep); background: var(--accent-soft); padding: .28rem .7rem; border-radius: 100px; font-weight: 600; }
.fp-cat[data-cat="Logro"] { color: #1f7a4d; background: #e2f4ea; }
.fp-cat[data-cat="Evento"] { color: #8a5a00; background: #f7ecd6; }
/* ==========================================================================
   REGLA GENERAL · EL TEXTO SE ADAPTA A SU CAJA, PERO NUNCA SE PARTEN PALABRAS
   (1-ago-2026, regla del cliente)
   --------------------------------------------------------------------------
   Caso que la motivó: en la tarjeta de un miembro, la biografía salía como
   "…actualizaciones en la p / agina web…". Lo causaba `word-break: break-all`,
   que autoriza al navegador a cortar por CUALQUIER letra aunque la palabra
   quepa entera en la línea siguiente.

   Las tres declaraciones no son intercambiables, y ahí está toda la diferencia:

     word-break: break-all        parte por cualquier letra, siempre.  ✗ NUNCA
                                  en texto que lee una persona.
     overflow-wrap: anywhere      parte una palabra si hace falta, PERO además
                                  cuenta esos cortes al calcular el ancho mínimo
                                  de la caja, así que en columnas estrechas parte
                                  de más.  ✗ evitar en prosa.
     overflow-wrap: break-word    parte una palabra SOLO si no cabe entera ni en
                                  su propia línea (una URL larga, un correo).
                                  ✓ ESTA es la que va en todo texto corrido.

   Excepción única: los ENLACES. Una URL no tiene espacios, así que si no se
   parte se sale de la caja; ahí sí vale cortar por donde sea (ver .rich-link,
   .bubble a, .task-link — cada uno lo lleva anotado).

   Y "adaptarse a la caja" es además no desbordarla: donde la altura es fija
   (tarjetas de un listado), el texto se recorta con puntos suspensivos usando
   -webkit-line-clamp, no dejando que empuje la tarjeta.
   ========================================================================== */
.app-body p, .app-body span, .app-body b, .app-body h1, .app-body h2,
.app-body h3, .app-body h4, .app-body li, .app-body td, .app-body div {
  overflow-wrap: break-word;
  word-break: normal;
}

.fp-title { font-family: var(--serif); font-weight: 400; font-size: 1.2rem; margin: .9rem 0 .4rem; overflow-wrap: break-word; word-break: normal; }
.fp-body { color: var(--ink-soft); font-size: .96rem; overflow-wrap: break-word; word-break: normal; text-align: justify; }
/* Enlaces detectados dentro del texto que ESCRIBE el usuario (publicaciones, comentarios, chat, tareas):
   se diferencian del texto con color coral + subrayado. En la burbuja propia (fondo coral) van en blanco. */
/* EXCEPCIÓN de la regla general: una URL no tiene espacios; si no se parte, se sale de la caja. */
.rich-link { color: var(--accent-deep); text-decoration: underline; text-underline-offset: 2px; overflow-wrap: break-word; }
.rich-link:hover { color: var(--accent); }
.bubble.mine .rich-link { color: #fff; }
/* Al abrir una publicación desde su notificación: un aro coral que aparece y se desvanece para ubicarla. */
.feed-post--flash { animation: feedFlash 1.8s ease; }
@keyframes feedFlash { 0%, 18% { box-shadow: 0 0 0 3px var(--accent); } 100% { box-shadow: 0 0 0 3px rgba(242,107,94,0); } }
.fp-foot { display: flex; gap: 1.6rem; margin-top: 1.1rem; padding-top: .9rem; border-top: 1px solid var(--line-2); color: var(--ink-mute); font-size: .84rem; }
.fp-act { display: inline-flex; align-items: center; gap: .4rem; transition: color .2s; }
.fp-act:hover { color: var(--ink); }
.fp-act.is-liked { color: var(--accent-deep); }
.fp-act.is-liked .app-icon { fill: var(--accent); stroke: var(--accent); }

.rail { display: grid; gap: 1.4rem; }
/* Las tarjetas de la columna derecha NUNCA se salen de su columna (10-ago-2026).
   Fallo real: entró al ranking «Maria De Los Angeles Leon Contreras» y la columna, que mide 320 px,
   se encontró con una tarjeta de 409 — el margen derecho apareció cortado. La causa es la de siempre
   en este proyecto: un hijo de una rejilla trae `min-width: auto`, o sea «no me encojas por debajo de
   mi contenido», y el nombre del ranking no puede partirse (lleva `white-space: nowrap` para poder
   cortarlo con puntos suspensivos). Entre las dos cosas, la tarjeta empujaba hacia fuera.
   Medido: con esta línea vuelve a 320 px exactos, y el nombre se corta con «…» como estaba pensado.
   Va SOLO en el rail, no en toda la app: un barrido global de esto ya rompió el pie de la portada. */
.rail > * { min-width: 0; }
.rail-card { padding: 1.2rem 1.3rem; }
.rail-card h3 { font-size: 1rem; font-weight: 600; margin-bottom: .9rem; display: flex; align-items: center; gap: .5rem; }
/* Tarjeta "La comunidad": nº real de miembros + descripción editable (lápiz, solo admin) */
.comm-side-stat { font-size: .86rem; margin-bottom: .7rem; }
.comm-side-stat b { font-family: var(--serif); font-style: italic; color: var(--accent-deep); font-size: 1.05rem; margin-right: .3rem; }
.comm-side-desc-row { display: flex; align-items: flex-start; gap: .5rem; }
.comm-side-desc { flex: 1 1 auto; font-size: .86rem; color: var(--ink-mute); margin: 0; }
.comm-side-edit { flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--line); background: var(--paper); color: var(--ink-soft); display: grid; place-items: center; cursor: pointer; transition: border-color .2s, color .2s; }
.comm-side-edit:hover { border-color: var(--accent); color: var(--accent); }
.comm-side-edit .app-icon { width: 14px; height: 14px; }
.leaderboard li { display: flex; align-items: center; gap: .7rem; padding: .5rem 0; }
.leaderboard li + li { border-top: 1px solid var(--line-2); }
.lb-rank { width: 22px; font-family: var(--serif); font-style: italic; color: var(--ink-mute); }
.lb-rank.top { color: var(--accent-deep); }
.leaderboard .side-avatar { width: 32px; height: 32px; font-size: .8rem; }
.leaderboard b { font-size: .86rem; }
.leaderboard .lb-id { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.leaderboard .lb-id b { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.leaderboard .lb-id small { font-size: .7rem; color: var(--ink-mute); }
.leaderboard .pts { margin-left: auto; flex: 0 0 auto; font-size: .8rem; font-weight: 600; color: var(--accent-deep); }
.leaderboard .lb-empty { padding: .3rem 0; }
.rail-event { display: flex; align-items: center; gap: .8rem; padding: .6rem .4rem; width: 100%; text-align: left; background: none; border: 0; border-radius: 10px; cursor: pointer; transition: background .2s; }
.rail-event:hover { background: var(--bg); }
.rail-event:hover .info b { color: var(--accent-deep); }
.rail-event + .rail-event { border-top: 1px solid var(--line-2); }
.rail-date { flex: 0 0 auto; width: 46px; text-align: center; background: var(--bg); border: 1px solid var(--line-2); border-radius: 10px; padding: .3rem 0; }
.rail-date .d { font-size: 1.1rem; font-weight: 600; line-height: 1; }
.rail-date .m { font-size: .64rem; color: var(--accent-deep); }
.rail-event .info b { font-size: .86rem; display: block; }
.rail-event .info span { font-size: .76rem; color: var(--ink-mute); }

/* =============================================================
   6. CALENDARIO
   ============================================================= */
/* UNA sola columna desde el 11-ago-2026: se retiró «Próximas sesiones» y el calendario se queda con
   todo el ancho (pedido del cliente). `min-width: 0` sigue haciendo falta —por defecto un hijo de
   grid no encoge por debajo de su contenido, y la tarjeta se salía de la pantalla con la barra
   lateral expandida—, y la tarjeta de «sin acceso» sigue ocupando la fila entera. */
.cal-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.4rem; align-items: start; min-width: 0; }
.cal-wrap { padding: 1.2rem 1.3rem; min-width: 0; }
.cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.1rem; }
.cal-head .m { font-family: var(--serif); font-weight: 300; font-size: 1.6rem !important; }
.cal-nav { display: flex; gap: .4rem; }
.cal-nav button { width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--line); background: var(--paper); display: grid; place-items: center; color: var(--ink-soft); }
.cal-nav button:hover { border-color: var(--ink); color: var(--ink); }
/* ⚠️ `repeat(7, 1fr)` a secas descuadraba el calendario en cuanto un día tenía evento: `1fr` lleva
   implícito min-width:auto, así que la píldora `.cal-evt` (white-space:nowrap, ej. "Sesión en vivo")
   ensanchaba SU columna y empujaba el domingo fuera de la tarjeta, desalineando todos los números.
   Salió a la luz recién el 27-jul-2026 porque hasta entonces ningún evento llegaba a guardarse
   (faltaba correr fase2/43) y la grilla nunca se había pintado con contenido real.
   `minmax(0,1fr)` en la pista + `min-width:0` en la celda dejan que el contenido se RECORTE
   (ya tiene overflow:hidden + ellipsis) en vez de estirar la columna. */
.cal-grid { display: grid; grid-template-columns: repeat(7,minmax(0,1fr)); gap: 4px; }
.cal-dow { text-align: center; font-size: .68rem; color: var(--ink-mute); padding: .4rem 0; font-weight: 600; min-width: 0; }
/* ⚠️ `overflow: hidden` en la celda es lo que impedía que cupieran 3 eventos: la celda es un ítem de
   grilla, y en un ítem de grilla `min-height: auto` (el tamaño mínimo automático = el del contenido)
   queda ANULADO en cuanto se recorta el desbordamiento. Sin ese recorte, `aspect-ratio: 1` pasa a ser
   el alto PREFERIDO —la celda sigue cuadrada mientras el contenido quepa— y la fila crece sola cuando
   un día trae 3 píldoras. El recorte horizontal que motivó el comentario de arriba no se pierde: la
   píldora ya tiene su propio overflow+ellipsis y la celda conserva `min-width: 0`. (28-jul-2026) */
/* ⚠️ `aspect-ratio: 1` se retiró el 11-ago-2026, al pasar el calendario a todo el ancho: con la
   tarjeta a ~1180px la celda medía 159x159 y quedaba un cuadrado enorme casi vacío, con una banda
   en blanco donde el mes no empieza en lunes. Ahora la celda es ANCHA y de alto contenido: parte de
   `min-height` y crece sola si el día trae varias píldoras. En el teléfono la celda vuelve a ser
   cuadrada (ver el bloque de móvil): ahí sí se necesita, porque las píldoras se vuelven puntos. */
.cal-day { min-height: 92px; border-radius: 10px; border: 1px solid transparent; padding: .4rem; font-size: .82rem; color: var(--ink-soft); position: relative; transition: background .2s, border-color .2s; min-width: 0; cursor: pointer; }
.cal-day:hover { background: var(--bg); }
.cal-day.muted { color: var(--line); cursor: default; }
.cal-day.muted:hover { background: transparent; }
.cal-day.today { border-color: var(--accent); color: var(--ink); font-weight: 600; }
.cal-day.has-event { background: var(--bg); }
/* Hasta 3 píldoras apiladas; de la cuarta en adelante, 3 + "+N" (regla 28-jul-2026). */
.cal-evts { display: flex; flex-direction: column; gap: 2px; margin-top: .22rem; min-width: 0; }
/* Mismo color de evento que la agenda del día (--evt-*): la píldora del calendario y el bloque de la
   agenda son lo mismo visto de dos maneras, así que comparten color. El borde de "hoy"
   (.cal-day.today) SÍ sigue en coral: es un indicador vivo, como la línea de "ahora". */
/* Hasta DOS líneas por píldora: desde que lleva el nombre del evento y no su tipo, una sola línea
   cortaba casi todos («Taller de storytelli…»). Con dos, la mayoría se lee entera; el que no cabe
   se sigue cortando con puntos suspensivos y el título completo está en el «title» del elemento. */
/* Un solo lavanda para todos los tipos: se probó un color por tipo y el cliente prefirió volver al
   color único, así que con él se fue también la leyenda (sin colores no decía nada). Lo que
   distingue un evento de otro es su NOMBRE, que es lo que se lee en la píldora. */
.cal-evt { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; font-size: .68rem; line-height: 1.32; background: var(--evt-soft); color: var(--evt-ink); border-radius: 5px; padding: .12rem .34rem; overflow: hidden; font-weight: 600; cursor: pointer; overflow-wrap: break-word; }

.events-list h3 { font-size: 1rem; font-weight: 600; margin-bottom: 1rem; }
.evt-item { display: flex; gap: 1rem; padding: 1rem; border-radius: 12px; transition: background .2s; }
.evt-item:hover { background: var(--bg); }
.evt-item + .evt-item { border-top: 1px solid var(--line-2); }
.evt-when { flex: 0 0 auto; width: 54px; text-align: center; }
.evt-when .d { font-family: var(--serif); font-size: 1.5rem; line-height: 1; }
.evt-when .m { font-size: .64rem; color: var(--evt-ink); }
.evt-info { flex: 1; min-width: 0; }
.evt-info .tag { font-size: .66rem; color: var(--evt-ink); font-weight: 600; }
.evt-info b { display: block; font-size: 1rem; margin: .15rem 0; }
.evt-info .det { font-size: .82rem; color: var(--ink-mute); display: flex; gap: 1rem; flex-wrap: wrap; }
.evt-join { flex: 0 0 auto; align-self: center; }
/* Agenda por día (opción H, 5-ago-2026): banda con la fecha + hora en columna fija con la serif. */
.evt-dia { background: var(--bg-3, #F4EFE6); background: color-mix(in srgb, var(--bg-3) 45%, var(--paper)); border-radius: 10px; padding: .5rem .9rem; font-size: .8rem; font-weight: 600; color: var(--ink-soft); margin: 1rem 0 .25rem; }
.evt-dia:first-child { margin-top: 0; }
.evt-agenda { display: grid; grid-template-columns: 118px minmax(0, 1fr) auto; gap: 1rem; align-items: center; padding: .8rem .9rem; }
.evt-agenda .evt-hora { font-family: var(--serif); font-size: .98rem; color: var(--ink); border-right: 2px solid var(--accent-soft); padding-right: .6rem; align-self: stretch; display: flex; align-items: center; }
.evt-agenda .evt-main b { display: block; font-size: .95rem; }
.evt-agenda .evt-sub { display: flex; gap: .9rem; margin-top: .25rem; font-size: .8rem; color: var(--ink-mute); flex-wrap: wrap; align-items: center; }
.evt-agenda .evt-sub span { display: inline-flex; align-items: center; gap: .35rem; }
.evt-agenda .evt-tipo { color: var(--accent-deep); font-weight: 600; }
.evt-agenda .evt-quien .evt-host-av { width: 22px; height: 22px; }
@media (max-width: 560px) {
  .evt-agenda { grid-template-columns: 86px minmax(0, 1fr); }
  .evt-agenda .evt-hora { font-size: .86rem; }
  .evt-agenda .evt-join { grid-column: 1 / -1; justify-self: stretch; text-align: center; }
}

/* =============================================================
   7. MÓDULOS (progreso)
   ============================================================= */
.plan-tabs { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: 1.6rem; }
.plan-tab { padding: .6rem 1.2rem; border-radius: 100px; border: 1px solid var(--line); background: var(--paper); font-size: .88rem; font-weight: 500; color: var(--ink-soft); transition: background-color .25s, border-color .25s, color .25s; }
.plan-tab:hover { border-color: var(--ink-mute); }
.plan-tab.is-active { background: var(--ink); color: var(--bg); border-color: var(--ink); }
/* Box "+" (miembro sin plan): teaser de los módulos de plan que llegan tras el onboarding */
/* El "+" mide EXACTAMENTE lo mismo que una cápsula de plan: mismo relleno vertical y misma altura de
   línea que `.plan-tab`. Antes traía `line-height: 1` y `padding: .5rem`, así que su caja propia era
   más baja y solo coincidía cuando el flex la estiraba — en cuanto caía en otra línea, se abría el
   desplegable de móvil o cambiaba la tipografía, quedaba de otro alto. Ahora coinciden por
   construcción; solo cambia el relleno horizontal, que sí es más estrecho a propósito. */
.plan-tab--plus { font-size: 1.15rem; font-weight: 600; padding: .6rem 1.5rem; border-style: dashed; color: var(--ink-mute); }
.plan-tab--plus:hover { border-color: var(--accent); color: var(--accent-deep); }

.plan-overview { padding: 1.4rem var(--card-pad); margin-bottom: 1.4rem; display: grid; grid-template-columns: 1fr; gap: 1.2rem; align-items: center; }
.plan-overview h2 { font-family: var(--serif); font-weight: 300; font-size: 1.7rem; }
.plan-overview p { color: var(--ink-mute); font-size: .9rem; margin-top: .3rem; }
.progress-ring { display: flex; align-items: center; gap: 1rem; }
.ring { --p: 0; width: 72px; height: 72px; border-radius: 50%; flex: 0 0 auto;
  background: conic-gradient(var(--accent) calc(var(--p)*1%), var(--bg-3) 0);
  display: grid; place-items: center; position: relative; }
.ring::before { content: ""; position: absolute; inset: 8px; border-radius: 50%; background: var(--paper); }
.ring .pct { position: relative; font-weight: 600; font-size: .95rem; }
.progress-ring .lbl b { font-size: .9rem; } .progress-ring .lbl span { font-size: .78rem; color: var(--ink-mute); display: block; }

.module { margin-bottom: 1rem; overflow: hidden; }
.module-num { width: 38px; height: 38px; border-radius: 11px; background: var(--bg); border: 1px solid var(--line-2); display: grid; place-items: center; font-family: var(--serif); font-style: italic; color: var(--accent-deep); flex: 0 0 auto; }
.module.is-done .module-num { background: var(--accent); color: #fff; border-color: var(--accent); }
.module-bar { width: 90px; height: 6px; border-radius: 100px; background: var(--bg-3); overflow: hidden; flex: 0 0 auto; }
.module-bar i { display: block; height: 100%; background: var(--accent); border-radius: inherit; transition: width .5s var(--ease-out); }
.module-lessons { display: none; padding: 0 1.3rem 1rem 1.3rem; }
.module.is-open .module-lessons { display: block; }

/* ===== Carrusel de portadas de módulos (Skool) ===== */
.mod-carousel { position: relative; margin-bottom: 1.4rem; }
.mod-track { display: flex; align-items: center; gap: 1rem; overflow-x: auto; scroll-snap-type: x proximity; padding: 1rem .6rem 1.2rem; -webkit-overflow-scrolling: touch; scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
.mod-track::-webkit-scrollbar { height: 8px; }
.mod-track::-webkit-scrollbar-thumb { background: var(--line); border-radius: 100px; }
.mod-track::-webkit-scrollbar-track { background: transparent; }
.mod-cover-card { flex: 0 0 280px; scroll-snap-align: center; background: var(--paper); border: 1px solid var(--line-2); border-radius: 16px; overflow: hidden; cursor: pointer; padding: 0; text-align: left; display: flex; flex-direction: column; transition: transform .22s var(--ease-out), box-shadow .22s; }
.mod-cover-card:hover { transform: translateY(-3px); box-shadow: 0 18px 40px -24px rgba(28,26,24,.45); }
/* Seleccionado: sin borde coral, se agranda un poco (efecto zoom tipo Dock de Mac) */
.mod-cover-card.is-active, .mod-cover-card.is-active:hover { transform: scale(1.08); box-shadow: 0 24px 54px -22px rgba(28,26,24,.5); position: relative; z-index: 2; }
.mod-cover-img { position: relative; aspect-ratio: 1000 / 516; background: var(--ink) center/cover no-repeat; }
/* Módulo sin portada Skool: estado neutral (NO una portada inventada) — fondo liso crema + título. */
.mod-cover-img--empty { background: linear-gradient(135deg, var(--bg-2), var(--paper)); display: grid; place-items: center; padding: 1rem 1.2rem; border-bottom: 1px solid var(--line-2); }
.mod-cover-empty { font: 600 1.02rem/1.25 var(--serif); color: var(--ink-soft); text-align: center; max-width: 90%; }
.mod-cover-num { position: absolute; top: .55rem; left: .6rem; min-width: 26px; height: 26px; padding: 0 .45rem; border-radius: 8px; background: rgba(28,26,24,.55); color: var(--paper); font: 600 .76rem/1 var(--sans); display: grid; place-items: center; backdrop-filter: blur(3px); }
/* Módulo OCULTO: los miembros no lo ven; el equipo sí, con la portada bajo una capa tenue (24-jul-2026). */
.mod-cover-card.is-mod-hidden { opacity: .45; filter: grayscale(.4); }
/* Descripción del módulo: bajo el título, dentro del módulo abierto (respeta saltos y enlaces). */
.mod-desc { font-size: .9rem; color: var(--ink-soft); margin: .1rem 0 .9rem; }
.mod-cover-check { position: absolute; top: .55rem; right: .6rem; width: 26px; height: 26px; border-radius: 50%; background: var(--accent); color: #fff; display: grid; place-items: center; box-shadow: 0 2px 8px -2px rgba(28,26,24,.5); }
.mod-cover-check .app-icon { width: 15px; height: 15px; }
.mod-cover-foot { padding: .7rem .85rem .85rem; display: flex; flex-direction: column; gap: .45rem; }
.mod-cover-foot .module-bar { width: 100%; }
.mod-cover-sub { font-size: .75rem; color: var(--ink-mute); }
/* Lápiz "gestionar grupos de módulos" — junto a la última pestaña de planes (solo coach/admin). */
.plan-tab-edit { display: inline-grid; place-items: center; width: 36px; height: 36px; border-radius: 50%; border: 1px solid var(--line); background: var(--paper); color: var(--ink-soft); cursor: pointer; flex: 0 0 auto; margin-left: .3rem; transition: border-color .18s, color .18s; }
.plan-tab-edit:hover { border-color: var(--accent); color: var(--accent); }
.plan-tab-edit .app-icon { width: 16px; height: 16px; }
/* Papelera de la barra: eliminar el plan/grupo activo por completo. Tono neutro; rojo al pasar encima. */
.plan-tab-del:hover { border-color: #d9534f; color: #d9534f; }
/* Reordenar módulos arrastrando. */
.mod-drag-row { cursor: grab; }
.mod-drag-row.dragging { opacity: .45; cursor: grabbing; }
.mod-drag-grip { display: inline-flex; align-items: center; color: var(--ink-mute); flex: 0 0 auto; }
.mod-drag-grip .app-icon { width: 15px; height: 15px; }
.mod-arrow { position: absolute; top: calc(50% - .5rem); transform: translateY(-50%); width: 38px; height: 38px; border-radius: 50%; border: 1px solid var(--line); background: var(--paper); color: var(--ink-soft); display: grid; place-items: center; cursor: pointer; z-index: 3; box-shadow: 0 8px 22px -12px rgba(28,26,24,.5); }
.mod-arrow:hover { border-color: var(--accent); color: var(--accent); }
.mod-arrow--prev { left: -6px; }
.mod-arrow--next { right: -6px; }
.mod-arrow .app-icon { width: 18px; height: 18px; }
/* Auto-scroll al arrastrar un módulo hacia el borde: la flecha es solo pista visual — "armada" (esperando)
   y "desplazando". En modo Mover no intercepta el arrastre (pointer-events:none), para que el dragover del
   track detecte la zona de borde de forma fiable incluso justo debajo de la flecha. */
.mods-moving .mod-arrow { pointer-events: none; }
.mod-arrow--armed { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.mod-arrow--scrolling { border-color: var(--accent); color: #fff; background: var(--accent); animation: modArrowPulse .7s ease-in-out infinite; }
@keyframes modArrowPulse { 0%,100% { box-shadow: 0 8px 22px -12px rgba(28,26,24,.5); } 50% { box-shadow: 0 0 0 5px var(--accent-soft); } }
@media (prefers-reduced-motion: reduce) { .mod-arrow--scrolling { animation: none; } }
@media (max-width: 640px) { .mod-arrow { display: none; } .mod-cover-card { flex-basis: 78vw; } }
/* Detalle del módulo seleccionado */
.mod-detail-block { background: var(--paper); border: 1px solid var(--line-2); border-radius: var(--radius); overflow: hidden; margin: 0; animation: viewIn .3s var(--ease-out); }
.mod-detail-head { display: flex; align-items: center; gap: 1rem; padding: 1.1rem 1.3rem; border-bottom: 1px solid var(--line-2); }
.mod-detail-head .t { flex: 1; min-width: 0; }
.mod-detail-head .t b { font-size: 1.05rem; display: block; }
.mod-detail-head .t span { font-size: .78rem; color: var(--ink-mute); }
.mod-detail-block .module-lessons { display: block; padding: 1rem 1.3rem 1.1rem; }
.mod-names-note { margin: 0; padding: 1rem 1.3rem 1.1rem; font-size: .85rem; color: var(--ink-mute); }
/* ---- Bloqueo de módulos por alumno (habilitación manual del coach) ---- */
.mod-cover-lock { position: absolute; top: .55rem; right: .6rem; width: 26px; height: 26px; border-radius: 50%; background: rgba(28,26,24,.62); color: var(--paper); display: grid; place-items: center; backdrop-filter: blur(3px); box-shadow: 0 2px 8px -2px rgba(28,26,24,.5); }
.mod-cover-lock .app-icon { width: 15px; height: 15px; }
.mod-cover-card.is-locked .mod-cover-img { filter: grayscale(.55) brightness(.92); }
.mod-cover-card.is-locked .mod-cover-img--empty { filter: none; opacity: .7; }
.mod-cover-card.is-locked .mod-cover-sub { color: var(--ink-soft); font-weight: 600; }
/* Aviso de módulo bloqueado en el detalle (vista del alumno) */
.module-locked { display: grid; place-items: center; text-align: center; gap: .5rem; padding: 2.4rem 1.3rem; }
.module-locked-ico { width: 52px; height: 52px; border-radius: 50%; background: var(--bg-2); color: var(--ink-soft); display: grid; place-items: center; }
.module-locked-ico .app-icon { width: 24px; height: 24px; }
.module-locked b { font: 600 1.05rem var(--serif); }
.module-locked p { margin: 0; max-width: 430px; font-size: .86rem; color: var(--ink-mute); }
/* Toggle de habilitación por módulo (Seguimiento Clientes) */
.mod-lock-toggle { flex: 0 0 auto; display: inline-flex; align-items: center; gap: .4rem; font: 600 .78rem var(--sans); padding: .45rem .8rem; border-radius: 100px; border: 1px solid var(--accent); background: var(--accent); color: #fff; cursor: pointer; transition: filter .18s, background .18s, color .18s, border-color .18s; }
.mod-lock-toggle .app-icon { width: 15px; height: 15px; }
.mod-lock-toggle:hover { filter: brightness(.96); }
.mod-lock-toggle.is-on { background: var(--accent-soft); color: var(--accent-deep); border-color: transparent; }
.mod-lock-toggle.is-on:hover { background: var(--bg-2); filter: none; }
/* "Desbloquear módulo completo": acción fuerte pero secundaria frente al toggle — va en fantasma
   para que el botón principal (habilitar/bloquear) siga siendo el que más llama la atención. */
.mod-unlock-all { flex: 0 0 auto; display: inline-flex; align-items: center; gap: .4rem; font: 600 .78rem var(--sans); padding: .45rem .8rem; border-radius: 100px; border: 1px solid var(--line); background: transparent; color: var(--ink-soft); cursor: pointer; transition: background .18s, color .18s, border-color .18s; }
.mod-unlock-all .app-icon { width: 15px; height: 15px; }
.mod-unlock-all:hover { background: var(--accent-soft); color: var(--accent-deep); border-color: transparent; }
/* Banner de módulo bloqueado para el alumno (Seguimiento Clientes) */
.mod-lock-banner { display: flex; align-items: center; gap: .5rem; background: var(--bg-2); color: var(--ink-soft); border: 1px dashed var(--line); border-radius: 10px; padding: .6rem .8rem; margin-bottom: .8rem; font-size: .82rem; }
.mod-lock-banner .app-icon { width: 16px; height: 16px; flex: 0 0 auto; }
.lesson { display: flex; align-items: center; gap: .9rem; padding: .8rem; border-radius: 10px; transition: background .2s; }
.lesson:hover { background: var(--bg); }
.lesson + .lesson { border-top: 1px solid var(--line-2); }
.lesson-check { width: 24px; height: 24px; border-radius: 50%; border: 1.7px solid var(--line); flex: 0 0 auto; display: grid; place-items: center; color: transparent; transition: background-color .25s, border-color .25s, color .25s; cursor: pointer; }
.lesson.done .lesson-check { background: var(--accent); border-color: var(--accent); color: #fff; }
.lesson-type { width: 30px; height: 30px; border-radius: 8px; background: var(--bg); display: grid; place-items: center; color: var(--ink-soft); flex: 0 0 auto; }
.lesson .t { flex: 1; min-width: 0; }
.lesson .t b { font-size: .9rem; font-weight: 500; display: block; }
.lesson.done .t b { color: var(--ink-mute); }
.lesson .t span { font-size: .74rem; color: var(--ink-mute); }

/* =============================================================
   8. PROGRAMACIÓN (Gantt)
   ============================================================= */
.gantt-legend { display: flex; gap: 1.2rem; flex-wrap: wrap; margin-bottom: 1.2rem; font-size: .8rem; color: var(--ink-mute); }
.gantt-legend span { display: inline-flex; align-items: center; gap: .4rem; }
.gantt-legend i { width: 12px; height: 12px; border-radius: 3px; }
.gantt-wrap { padding: var(--card-pad); overflow-x: auto; }
.gantt { min-width: calc(220px + var(--cols, 12) * 46px); }
.gantt-row { display: grid; grid-template-columns: 220px 1fr; gap: 0; align-items: center; }
.gantt-row.head { border-bottom: 1px solid var(--line); padding-bottom: .6rem; margin-bottom: .6rem; }
.gantt-row.months { margin-bottom: .2rem; }
.gantt-months { display: grid; grid-template-columns: repeat(var(--months, 4), 1fr); }
/* Mismo degradado negro→coral que el topbar (var(--grad-portada)), texto blanco. */
.gantt-months .mo { text-align: center; font-size: .72rem; font-weight: 600; color: #fff; padding: .25rem 0; background: var(--grad-portada); border-radius: 6px; margin: 0 2px; }
.gantt-weeks { display: grid; grid-template-columns: repeat(var(--cols, 12), 1fr); }
.gantt-weeks .wk { font-size: .68rem; color: var(--ink-mute); text-align: center; padding: 0 2px; }
.gantt-task { padding: .7rem .8rem .7rem 0; }
.gantt-task b { font-size: .88rem; display: block; }
.gantt-task span { font-size: .72rem; color: var(--ink-mute); }
.gantt-track { position: relative; height: 30px; display: grid; grid-template-columns: repeat(var(--cols, 12), 1fr); align-items: center; }
.gantt-track::before { content: ""; position: absolute; inset: 0; background-image: repeating-linear-gradient(to right, transparent, transparent calc(100% / var(--cols, 12) - 1px), var(--line-2) calc(100% / var(--cols, 12) - 1px), var(--line-2) calc(100% / var(--cols, 12))); }
/* La barra se posiciona en % sobre el total de DÍAS del cronograma (antes ocupaba columnas enteras de
   semana con grid-column). Así una etapa puede durar 3 o 10 días exactos y el editor de cronograma
   puede fijar el día de inicio, mientras la grilla de fondo sigue marcando semanas. */
.gantt-bar { position: absolute; top: 50%; transform: translateY(-50%); height: 18px; border-radius: 100px;
  display: flex; align-items: center; padding: 0 .5rem; font-size: .66rem; font-weight: 600; color: #fff;
  white-space: nowrap; overflow: hidden; min-width: 8px; }
.gantt-bar.done { background: #1f7a4d; }
.gantt-bar.doing { background: var(--accent); }
.gantt-bar.todo { background: var(--bg-3); color: var(--ink-mute); }
.gantt-row.body { cursor: pointer; border-radius: 8px; transition: background .15s; position: relative; z-index: 1; }
.gantt-row.body:hover { background: var(--bg); }
.gantt-row.body:hover .gantt-task b { color: var(--accent); }
.gantt-body { position: relative; }
/* PAUSA ACTIVA · VELO (30-jul-2026, extendido al calendario).
   El alumno sigue viendo su cronograma —y ahora también su calendario— bajo un velo tenue, con el
   banner negro-coral encima. Se prefirió al bloqueo opaco: ver lo que hay detrás dice «sigue aquí
   esperándote» mejor que una tarjeta que lo tapa. El velo se come los clics, así que no se interactúa
   con lo de debajo. Las mismas clases sirven para las dos secciones. */
.pausa-velada { position: relative; }
.pausa-velo { position: absolute; inset: 0; z-index: 40; border-radius: 16px;
  background: rgba(244, 241, 234, .45); backdrop-filter: blur(1.5px); -webkit-backdrop-filter: blur(1.5px); }
.pausa-banner { position: absolute; top: 150px; left: 50%; transform: translateX(-50%); z-index: 41;
  display: flex; flex-direction: column; align-items: center; gap: .3rem; text-align: center;
  /* Brillo coral sutil en la esquina superior derecha, con la paleta del --grad-box de la casa. */
  background: radial-gradient(circle at 100% 0%, rgba(199, 92, 79, .5) 0%, rgba(111, 58, 50, .26) 30%, rgba(28, 26, 24, 0) 62%), #1C1A18;
  color: #F4F1EA; border-radius: 16px; padding: 1.15rem 1.6rem;
  width: min(92%, 430px); box-shadow: 0 18px 44px rgba(28, 26, 24, .3); }
.pausa-banner b { font-size: 1.02rem; color: #fff; }
.pausa-banner > span:last-of-type { font-size: .85rem; opacity: .85; }
.pausa-banner-ico { display: flex; width: 34px; height: 34px; border-radius: 50%; background: var(--accent);
  color: #fff; align-items: center; justify-content: center; margin-bottom: .15rem; }
.pausa-banner-ico .app-icon { width: 17px; height: 17px; }
.pausa-banner-cancel { margin-top: .55rem; border-color: rgba(244, 241, 234, .45); color: #F4F1EA; background: transparent; }
.pausa-banner-cancel:hover { background: rgba(244, 241, 234, .14); border-color: rgba(244, 241, 234, .7); color: #fff; }
/* En pantallas bajas el banner no debe quedar fuera de la vista. */
@media (max-height: 700px) { .pausa-banner { top: 80px; } }
/* Banda de «mentoría finalizada» en Mensajes (opción C del cliente): no tapa nada. El alumno sigue
   leyendo sus conversaciones y la banda explica por qué ya no puede escribir. Mismo lenguaje que el
   banner del velo —fondo carbón, icono coral— para que se lean como lo mismo dicho de dos formas. */
.fin-banda { display: flex; align-items: center; gap: .8rem; background: var(--ink); color: #F4F1EA;
  border-radius: 14px; padding: .9rem 1.1rem; margin-bottom: .9rem; }
.fin-banda-ico { display: flex; width: 32px; height: 32px; border-radius: 50%; background: var(--accent);
  align-items: center; justify-content: center; flex: 0 0 auto; }
.fin-banda-ico .app-icon { width: 16px; height: 16px; color: #fff; }
.fin-banda b { font-size: .94rem; color: #fff; display: block; }
.fin-banda span { font-size: .82rem; opacity: .85; }
.fin-banda .btn { margin-left: auto; flex: 0 0 auto; border-color: rgba(244,241,234,.45); color: #F4F1EA; background: transparent; }
.fin-banda .btn:hover { background: rgba(244,241,234,.14); border-color: rgba(244,241,234,.7); color: #fff; }
@media (max-width: 560px) {
  .fin-banda { flex-wrap: wrap; }
  .fin-banda .btn { margin-left: 0; width: 100%; justify-content: center; }
}

/* Y los avisos EN LÍNEA, donde estaría la caja de escribir (comunidad, chat y comentarios). */
.pausa-aviso { display: flex; align-items: center; gap: .9rem; padding: .9rem 1.2rem; margin-bottom: 1rem; border-left: 3px solid var(--accent); }
.pausa-aviso > div { flex: 1; min-width: 0; }
.pausa-aviso b { display: block; font-size: .92rem; }
.pausa-aviso span { display: block; font-size: .84rem; color: var(--ink-mute); margin-top: .1rem; }
.pausa-aviso-ico { flex: 0 0 auto; display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; background: var(--accent-soft); color: var(--accent-deep); }
.pausa-aviso-ico .app-icon { width: 17px; height: 17px; }
.pausa-aviso .btn { flex: 0 0 auto; }
/* En el chat va pegado abajo, donde estaría el compositor: mismo borde superior que .conv-form. */
.conv-paused { display: flex; align-items: center; gap: .9rem; padding: 1rem 1.3rem; border-top: 1px solid var(--line-2); }
.conv-paused > div { flex: 1; min-width: 0; }
.conv-paused b { display: block; font-size: .92rem; }
.conv-paused span { display: block; font-size: .84rem; color: var(--ink-mute); margin-top: .1rem; }
.conv-paused .app-icon { flex: 0 0 auto; width: 20px; height: 20px; color: var(--accent-deep); }
.conv-paused .btn { flex: 0 0 auto; }
/* Bajo los comentarios de una publicación, donde estaría el campo para comentar. */
/* Dentro de una tarea que valida el coach, cuando el alumno está en pausa. */
.task-paused { display: flex; align-items: flex-start; gap: .5rem; margin: .5rem 0 .1rem; padding: .55rem .75rem;
  border-radius: 10px; background: var(--accent-soft); color: var(--ink-soft); font-size: .84rem; line-height: 1.45; }
.task-paused .app-icon { width: 15px; height: 15px; color: var(--accent-deep); flex: 0 0 auto; margin-top: .12rem; }
.fp-comment-paused { display: flex; align-items: center; gap: .5rem; padding: .7rem .2rem .2rem; color: var(--ink-mute); font-size: .85rem; }
.fp-comment-paused .app-icon { width: 15px; height: 15px; color: var(--accent-deep); flex: 0 0 auto; }
@media (max-width: 560px) {
  .pausa-aviso, .conv-paused { flex-wrap: wrap; }
  .pausa-aviso .btn, .conv-paused .btn { width: 100%; justify-content: center; }
}
.prog-admin-note { display: inline-flex; align-items: center; gap: .35rem; margin-top: .6rem; font-size: .8rem; color: var(--accent); }
.prog-admin-note .app-icon { width: 14px; height: 14px; }
/* Fecha de término del cronograma (con la pausa ya sumada) en el intro del cronograma. */
.prog-dates { margin: .55rem 0 0; font-size: .9rem; color: var(--ink-soft); }
.prog-pause-badge { display: inline-flex; align-items: center; gap: .3rem; margin-left: .35rem; padding: .1rem .5rem;
  border-radius: 100px; background: var(--accent-soft); color: var(--accent-deep); font-size: .74rem; font-weight: 600; white-space: nowrap; }
.prog-pause-badge .app-icon { width: 12px; height: 12px; }
/* Fila "Pausa aplicada" en la proyección de Seguimiento. */
.tm-row--pause span { display: inline-flex; align-items: center; gap: .3rem; color: var(--accent-deep); }
.tm-row--pause b { color: var(--accent-deep); }
.tm-row--pause .app-icon { width: 13px; height: 13px; }
/* El resalte coral del módulo al llegar desde el cronograma (.module--flash / @keyframes modFlash) se
   ELIMINÓ a pedido del cliente (17-jul-2026). La navegación desde el cronograma sigue igual: el módulo
   destino se selecciona y queda a la vista, solo se quitó el parpadeo del borde. */

/* =============================================================
   9. PERFIL
   ============================================================= */
.profile-layout { display: grid; grid-template-columns: 1fr; gap: 1.4rem; align-items: start; }
.profile-hero { padding: 0; overflow: hidden; }
.profile-cover { height: 140px; background: linear-gradient(120deg, var(--accent-soft), var(--bg-3)); position: relative; }
/* Portada propia (5-ago-2026): la imagen cubre el encabezado; sin imagen, el degradado de arriba.
   El velo del ::after se apaga para no lavar la foto. */
.profile-cover.has-img { background-size: cover; background-position: center; }
.profile-cover.has-img::after { display: none; }
.cover-upload { display: grid; gap: .6rem; }
.cover-upload-prev { height: 90px; border-radius: 10px; border: 1px dashed var(--line); background: linear-gradient(120deg, var(--accent-soft), var(--bg-3)); display: grid; place-items: center; font-size: .78rem; color: var(--ink-mute); }
.cover-upload-prev.has-img { background-size: cover; background-position: center; border-style: solid; color: transparent; }
.profile-cover::after { content: ""; position: absolute; inset: 0; background: radial-gradient(60% 100% at 80% 0%, var(--accent-glow), transparent 70%); }
.profile-id { padding: 0 1.5rem 1.5rem; margin-top: -52px; position: relative; }
/* 92 → 106px (4-ago-2026, pedido del cliente). Sin riesgo de calidad: las fotos se guardan a
   512px (resizeImage), casi cinco veces este tamaño. El solape sobre la portada crece a juego. */
.profile-avatar { width: 106px; height: 106px; border-radius: 50%; border: 4px solid var(--paper); background: var(--accent-soft); display: grid; place-items: center; color: var(--accent-deep); font-family: var(--serif); font-style: italic; font-size: 2.5rem; font-weight: 600; }
.profile-id h2 { font-family: var(--serif); font-weight: 300; font-size: 1.7rem; margin-top: .8rem; }
.profile-id .role { color: var(--ink-mute); font-size: .9rem; }
/* Sin tope de ancho (5-ago-2026, pedido del cliente): el texto llega al borde derecho del box,
   con el mismo margen que el izquierdo — el padding del contenedor manda por los dos lados. */
.profile-id .bio { color: var(--ink-soft); font-size: .94rem; margin-top: .8rem; text-align: justify; }
.profile-meta { display: flex; gap: 1.4rem; flex-wrap: wrap; margin-top: 1rem; font-size: .82rem; color: var(--ink-mute); }
.profile-meta span { display: inline-flex; align-items: center; gap: .4rem; }
/* Presentación del alumno (fase2/68) */
/* Justificado (4-ago-2026): de inline-flex a block — un texto de varias líneas no se puede
   justificar dentro de un flex en fila; el icono queda en línea con la primera. */
/* «Ayuda a…» · el icono y el texto en la MISMA línea, y el texto sigue debajo del icono cuando no
   cabe en una (12-ago-2026, pedido del cliente). Antes era `display: block` con el icono como parte
   del flujo del texto: en cuanto la frase pasaba de una línea, el icono quedaba suelto arriba.
   `justify` fuera: con una sola línea corta, justificar separa las palabras hasta el ridículo. */
.profile-helps { color: var(--accent-deep); font-size: .9rem; font-weight: 600; margin-top: .7rem;
  display: flex; align-items: flex-start; gap: .4rem; text-align: left; }
.profile-helps .app-icon { flex: none; margin-top: .15rem; }   /* no se encoge ni se descuelga al crecer el texto */
.profile-ig { display: inline-flex; align-items: center; gap: .4rem; color: var(--ink-mute); text-decoration: none; transition: color .25s; }
.profile-ig:hover { color: var(--accent-deep); }
/* Editar perfil: lápiz flotante (esquina inferior derecha del box de perfil) → abre modal */
.profile-edit-fab {
  position: absolute; right: 1.4rem; bottom: 1.5rem; width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--paper); color: var(--ink-soft); display: grid; place-items: center;
  cursor: pointer; transition: border-color .2s, color .2s;
}
.profile-edit-fab:hover { border-color: var(--accent); color: var(--accent); }

.profile-side { display: grid; gap: 1.4rem; }
.profile-stats { padding: 1.3rem; }
.profile-stats .row { display: flex; justify-content: space-between; align-items: center; padding: .6rem 0; }
.profile-stats .row + .row { border-top: 1px solid var(--line-2); }
.profile-stats .row b { font-family: var(--serif); font-style: italic; font-size: 1.3rem; color: var(--accent-deep); }
.profile-stats .row span { font-size: .86rem; color: var(--ink-soft); }

/* Edit form */
.field { margin-bottom: 1.1rem; }
.field label { display: block; font-size: .78rem; font-weight: 600; color: var(--ink-soft); margin-bottom: .4rem; letter-spacing: .02em; }
.field input, .field textarea, .field select {
  width: 100%; padding: .75rem 1rem; border-radius: 12px; border: 1px solid var(--line);
  background: var(--bg); font: inherit; font-size: .92rem; color: var(--ink); transition: border-color .25s, background .25s;
}
.field input:focus, .field textarea:focus, .field select:focus { outline: none; border-color: var(--accent); background: var(--paper); }
.field textarea { resize: vertical; min-height: 90px; }
/* Desplegables en formato píldora, igual que el filtro "Categoría" de Miembros (.members-select)
   — los input/textarea de al lado conservan su radio de 12px, solo cambian los <select>. */
.field select {
  border-radius: 100px; padding-right: 2.6rem; cursor: pointer;
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237a736b' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 1rem center; background-size: 14px;
}
.field .hint { font-size: .72rem; color: var(--ink-mute); margin-top: .35rem; }
.avatar-upload { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.2rem; }
.avatar-upload-actions { display: flex; align-items: center; flex-wrap: wrap; gap: .6rem; }
.avatar-upload-actions .hint { margin: 0 0 0 .3rem; color: var(--ink-mute); }
.avatar-upload .preview { width: 64px; height: 64px; border-radius: 50%; background: var(--accent-soft); display: grid; place-items: center; color: var(--accent-deep); font-family: var(--serif); font-style: italic; font-size: 1.5rem; font-weight: 600; flex: 0 0 auto; }
.edit-actions { display: flex; gap: .7rem; align-items: center; margin-top: 1.4rem; }

/* ---------- Paso de perfil obligatorio (31-jul-2026) ----------
   Va DESPUÉS de .field, .avatar-upload y .edit-actions a propósito: todo lo de abajo son ajustes
   sobre esas reglas base y, puesto antes, se ignoraría en silencio (la trampa de cascada de
   siempre). El modal se marca .modal-locked, pero el bloqueo real lo hace closeModal() en el JS:
   el CSS no puede impedir que alguien cierre algo. */
.pgate-head { text-align: center; margin-bottom: 1.6rem; }
.pgate-head h2 { font-family: var(--serif); font-weight: 300; font-size: 1.6rem; margin-top: .2rem; }
.pgate-head p { color: var(--ink-soft); font-size: .92rem; margin: .6rem auto 0; max-width: 46ch; }
.pgate .avatar-upload { justify-content: center; padding: 1.1rem; border: 1px dashed var(--line-2); border-radius: 18px; margin-bottom: .5rem; }
.pgate .avatar-upload .preview { width: 76px; height: 76px; font-size: 1.8rem; }
/* En el móvil la fila envuelve fea: la foto queda a la izquierda y el botón arriba a la derecha,
   con la ayuda descolgada debajo. Apilada se lee de una. Va DESPUÉS de la regla que ajusta, no
   antes (los overrides ≤560px puestos arriba se ignoran en silencio). */
@media (max-width: 560px) {
  .pgate .avatar-upload { flex-direction: column; text-align: center; gap: .8rem; }
  .pgate .avatar-upload-actions { justify-content: center; }
  .pgate .avatar-upload-actions .hint { margin-left: 0; }
}
/* El aviso de error usa el coral de la marca, no un rojo de sistema: es una falta que completar,
   no un fallo. Oculto de base; el JS solo alterna [hidden]. */
.pgate-err { color: var(--accent-deep); font-size: .78rem; margin: .35rem 0 0; }
.pgate-err[hidden] { display: none; }
/* El de la foto es el único que NO va dentro de un .field, así que sin este respiro queda pegado a
   la etiqueta de abajo y se lee como si fuera el error de ese otro campo (visto en preview). */
.pgate .avatar-upload + .pgate-err { margin: .2rem 0 1.1rem; }
.pgate .field label .req { font-family: inherit; font-style: normal; font-size: .7rem; color: var(--accent-deep); font-weight: 600; }
.pgate-sep { display: flex; align-items: center; gap: .8rem; margin: 1.6rem 0 1.2rem; color: var(--ink-mute); font-size: .74rem; }
.pgate-sep::before, .pgate-sep::after { content: ""; height: 1px; background: var(--line-2); flex: 1 1 auto; }
.pgate-sep span { flex: 0 0 auto; }
.pgate .edit-actions { margin-top: 1.6rem; }
.pgate .edit-actions .btn { width: 100%; justify-content: center; }

/* =============================================================
   10. PANEL DE MÉTRICAS (restringido)
   ============================================================= */
.restricted-banner { display: flex; align-items: center; flex-wrap: wrap; gap: .8rem; padding: .9rem 1.2rem; border-radius: 12px; background: var(--ink); color: var(--bg); margin-bottom: 1.6rem; font-size: .88rem; }
.restricted-banner .app-icon { stroke: var(--accent); flex: 0 0 auto; }
.restricted-banner b { color: var(--accent); }
/* Franja de coach sobre el perfil de un alumno (29-jul-2026): atajo a "Gestionar avance y módulos"
   para quien es SU coach asignado. Reusa .restricted-banner —el formato del aviso de "agenda tu
   onboarding"— para que todos los avisos de barra del portal se lean igual. Diferencia: allá la acción
   es texto con flecha y el banner entero es el enlace; acá es un BOTÓN coral dentro de la franja, así
   que la franja es un <div> (un botón dentro de otro botón no es HTML válido).
   ⚠️ Va JUSTO AQUÍ, después de la regla base: puesto antes, el `padding` de arriba (atajo, no
   longhand) le ganaba y el recorte vertical se ignoraba en silencio. Un botón es más alto que una
   línea de texto, y sin este recorte la franja quedaba 16px más alta que la de onboarding. */
.restricted-banner--action { padding-top: .5rem; padding-bottom: .5rem; }
.restricted-banner .banner-btn { margin-left: auto; flex: 0 0 auto; }
/* Dos gráficos del panel (Recorrido + Tiempo activo), lado a lado y del tamaño del box "Potencial upsell" (mitad de ancho). */
.metrics-charts { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.2rem; margin-bottom: 1.4rem; align-items: stretch; }
/* Ambas cards igualan altura (stretch) y empujan su gráfico al fondo, de modo que los dos SVG quedan alineados abajo. */
.metrics-chart-card, .metrics-charts .metrics-time-card { display: flex; flex-direction: column; }
.metrics-chart-card .metrics-chart, .metrics-charts .metrics-time-card .metrics-chart { margin-top: auto; }
.metrics-chart-card .metrics-chart-sub, .metrics-charts .metrics-time-card .metrics-chart-sub { min-height: 4.6em; }
.metrics-chart-card { padding: 1.3rem var(--card-pad) 1.4rem; color: var(--paper); border: 0; overflow: hidden; background: var(--grad-portada); }
.metrics-chart-card h3, .metrics-stats-card h3 { font-size: 1.05rem; font-weight: 600; color: var(--paper); }
.metrics-stats-card { padding: 1.1rem var(--card-pad); color: var(--paper); border: 0; overflow: hidden; background: var(--grad-portada); }
.metrics-stats-card .mhero-stats { grid-template-columns: repeat(2, 1fr); height: 100%; }
.metrics-chart-sub { font-size: .82rem; color: rgba(244,241,234,.72); margin: .2rem 0 .9rem; }
.metrics-chart { width: 100%; height: auto; display: block; }
.mhero-stats { display: grid; grid-template-columns: 1fr 1fr; gap: .7rem; align-content: center; }
/* Todas las tarjetas del mismo tamaño; si el total es impar, la última queda sola en su fila a tamaño normal. */
.mhero-stat { background: rgba(244,241,234,.08); border: 1px solid rgba(244,241,234,.16); border-radius: 12px; padding: .8rem .9rem; }
.mhero-stat .n { font-size: 1.6rem; font-weight: 600; line-height: 1; }
.mhero-stat .l { font-size: .8rem; color: rgba(244,241,234,.9); margin-top: .35rem; }
.mhero-stat .s { font-size: .68rem; color: rgba(244,241,234,.55); }
@media (min-width: 880px) { .metrics-charts { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
/* Equipo: tabla (Métricas por coach) ARRIBA y gráfico (Tiempo activo) ABAJO, ambos a todo el ancho.
   Se apilan en 1 columna (la tabla va primero en el DOM → queda arriba). Regla posterior → gana sobre el 2-col. */
.metrics-charts--team { grid-template-columns: minmax(0, 1fr); }
.metrics-charts--team .metrics-coach-card { margin-bottom: 0; } /* separación por el gap del grid, no por margen propio */
@media (max-width: 640px) { .metrics-stats-card .mhero-stats { grid-template-columns: 1fr 1fr; } }
.mc-grid { stroke: rgba(244,241,234,.16); stroke-width: 1; }
.mc-legend { display: flex; flex-wrap: wrap; gap: 1.1rem; margin-bottom: .6rem; }
.mc-leg { display: inline-flex; align-items: center; gap: .4rem; font-size: .8rem; color: rgba(244,241,234,.9); }
.mc-leg i { width: 13px; height: 13px; border-radius: 3px; display: inline-block; }
/* Gráfico de recorrido: el SVG se estira a todo el ancho (preserveAspectRatio=none, alto fijo por CSS)
   y TODO el texto de ejes es HTML a tamaño fijo → tipografía constante e igual a la de la página. */
.mc-chart { margin-top: .5rem; }
/* margen izq. = sitio de las etiquetas Y (hasta 3 dígitos con ~300 alumnos); margen der. = medio
   ancho de la última etiqueta X (M12) para que no se corte contra el borde de la tarjeta. */
.mc-plot { position: relative; height: 210px; margin: 0 16px 0 42px; }
.mc-svg { width: 100%; height: 100%; display: block; overflow: visible; }
.mc-yl { position: absolute; right: 100%; margin-right: 8px; transform: translateY(-50%); font-size: .8rem; line-height: 1; color: rgba(244,241,234,.6); font-variant-numeric: tabular-nums; white-space: nowrap; }
.mc-dot { position: absolute; width: 7px; height: 7px; border-radius: 50%; transform: translate(-50%, -50%); }
.mc-xaxis { position: relative; height: 1rem; margin: .5rem 16px 0 42px; }
.mc-xl { position: absolute; transform: translateX(-50%); font-size: .8rem; line-height: 1; color: rgba(244,241,234,.6); white-space: nowrap; }
/* Card de recorrido SUELTA (no en grid): el subtítulo no necesita el min-height que igualaba alturas. */
.bit-chart-full .metrics-chart-sub { min-height: 0; }
.metrics-flags { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.2rem; margin-bottom: 1.4rem; }
.metrics-flag-card { padding: 1.1rem var(--card-pad) .7rem; }
.metrics-flag-card h3 { display: flex; align-items: center; gap: .45rem; font-size: .98rem; font-weight: 600; margin-bottom: .4rem; }
.metrics-flag-card h3 .app-icon { width: 17px; height: 17px; color: var(--accent); }
.flag-count { margin-left: auto; font-size: .78rem; color: var(--ink-mute); font-weight: 600; }
.flag-row { display: flex; align-items: center; gap: .7rem; padding: .6rem .4rem; border-top: 1px solid var(--line-2); cursor: pointer; border-radius: 8px; }
.flag-row:hover { background: var(--bg); }
.flag-id { flex: 1; min-width: 0; }
.flag-id b { display: block; font-size: .9rem; }
.flag-id span { font-size: .76rem; color: var(--ink-mute); }
.flag-tag { font-size: .7rem; font-weight: 600; padding: .2rem .55rem; border-radius: 100px; flex: 0 0 auto; }
.flag-tag.up { background: #e2f4ea; color: #1f7a4d; }
.flag-tag.warn { background: var(--accent-soft); color: var(--accent-deep); }
.flag-av, .coachm-av { width: 38px; height: 38px; flex: 0 0 auto; border-radius: 50%; background: var(--bg-3) center/cover no-repeat; color: var(--ink-soft); display: grid; place-items: center; font-weight: 600; font-size: .8rem; }
.metrics-coach-card { padding: 1.2rem var(--card-pad); margin-bottom: 1.4rem; }
.metrics-coach-card h3 { font-size: 1rem; font-weight: 600; margin-bottom: .8rem; }
.coachm-table { border: 1px solid var(--line-2); border-radius: 12px; overflow: hidden; min-width: 460px; }
.coachm-row { display: grid; grid-template-columns: 1.8fr .8fr 1fr .9fr; gap: .8rem; align-items: center; padding: .7rem var(--card-pad); border-top: 1px solid var(--line-2); }
.coachm-row:first-child { border-top: 0; }
.coachm-head { background: var(--bg); font-size: .72rem; color: var(--ink-mute); font-weight: 600; }
.coachm-name { display: flex; align-items: center; gap: .6rem; min-width: 0; }
.coachm-name b { font-size: .9rem; }
.coachm-stat b { font-size: 1.05rem; display: block; }
.coachm-stat span { font-size: .72rem; color: var(--ink-mute); }
.metrics-empty { color: var(--ink-mute); font-size: .86rem; padding: .6rem 0; }
.metrics-empty.light { color: rgba(244,241,234,.72); }
/* Tiempo activo de coaches — gráfico de barras en card oscura premium */
.metrics-time-card { padding: 1.3rem var(--card-pad) 1.4rem; color: var(--paper); border: 0; overflow: hidden; background: var(--grad-portada); }
.metrics-time-card h3 { font-size: 1.05rem; font-weight: 600; color: var(--paper); }
/* Barras horizontales del tiempo activo del equipo, en HTML (no SVG): nombre · barra · valor.
   Una sola grilla compartida (filas con display:contents) → columnas alineadas entre todas las filas
   y el valor comparte ancho. La tipografía la hereda del área (14px), no se fija acá. */
.tact-chart { display: grid; grid-template-columns: minmax(84px, 168px) 1fr auto; gap: .55rem .75rem; align-items: center; margin-top: .2rem; }
.tact-row { display: contents; }
.tact-name { min-width: 0; font-weight: 600; color: rgba(244,241,234,.96); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tact-track { height: 15px; border-radius: 7px; background: rgba(244,241,234,.14); overflow: hidden; }
.tact-bar { display: block; height: 100%; border-radius: 7px; background: linear-gradient(90deg, #F0A48F 0%, #F26B5E 100%); }
.tact-val { color: var(--paper); font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
/* Encabezados de sección del Panel de métricas (Comunidad / Equipo) */
.metrics-section-head { margin: 0 0 1rem; }
.metrics-section-head h2 { font-size: 1.2rem; font-weight: 600; }
.metrics-section-head p { font-size: .85rem; color: var(--ink-mute); margin-top: .15rem; }
.metrics-section-head.is-team { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid var(--line-2); }
/* Métricas por coach compactas (al tamaño del box de tiempo activo) */
.metrics-coach-card--compact { margin-bottom: 0; padding: 1.1rem var(--card-pad) .9rem; }
.metrics-coach-card--compact h3 { font-size: .98rem; margin-bottom: .55rem; }
.metrics-coach-card--compact .coachm-row { grid-template-columns: 1.6fr .55fr .55fr .5fr; gap: .35rem; padding: .5rem .65rem; }
.metrics-coach-card--compact .coachm-av { width: 27px; height: 27px; font-size: .66rem; }
.metrics-coach-card--compact .coachm-name { gap: .45rem; }
.metrics-coach-card--compact .coachm-name b { font-size: .78rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.metrics-coach-card--compact .coachm-stat b { font-size: .9rem; }
.metrics-coach-card--compact .coachm-stat span { font-size: .6rem; }
.metrics-coach-card--compact .coach-dash-note { margin-top: .5rem; font-size: .68rem; }
.coachm-head-row { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .55rem; }
.coachm-head-row h3 { margin-bottom: 0; }
.coachm-range { font-size: .72rem; padding: .26rem 1.5rem .26rem .5rem; border: 1px solid var(--line-2); border-radius: 100px; background: var(--paper); color: var(--ink-soft); cursor: pointer; max-width: 150px; appearance: none; -webkit-appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237a736b' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right .45rem center; background-size: 11px; }
@media (min-width: 880px) { .metrics-flags { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
.bars { display: flex; align-items: flex-end; gap: .6rem; height: 180px; }

/* =============================================================
   Mobile drawer overlay
   ============================================================= */
.app-overlay { position: fixed; inset: 0; background: rgba(28,26,24,.4); z-index: 75; opacity: 0; visibility: hidden; transition: opacity .35s; }
body.drawer-open .app-overlay { opacity: 1; visibility: visible; }

/* =============================================================
   11. RESPONSIVE
   ============================================================= */
@media (min-width: 600px) {
}
@media (min-width: 980px) {
  .app-main { transition: margin-left .4s var(--ease-out); }
  .sidebar { transition: width .4s var(--ease-out), transform .45s var(--ease-out), padding .4s var(--ease-out); }
  /* ----- Mini-barra colapsada: solo iconos ----- */
  body.sidebar-collapsed .sidebar { width: 76px; padding-left: .5rem; padding-right: .5rem; }
  body.sidebar-collapsed .app-main { margin-left: 76px; }
  body.sidebar-collapsed .sidebar-section-label { opacity: 0; height: 0; padding: 0; margin: 0; overflow: hidden; }
  body.sidebar-collapsed .sidebar-brand { justify-content: center; padding-left: 0; padding-right: 0; }
  body.sidebar-collapsed .sidebar-brand img.brand-full { display: none; }
  body.sidebar-collapsed .sidebar-brand img.brand-mini { display: block; margin: 0 auto; }
  body.sidebar-collapsed .sidebar .side-link,
  body.sidebar-collapsed .side-home,
  body.sidebar-collapsed .side-logout { justify-content: center; gap: 0; font-size: 0 !important; padding-left: 0; padding-right: 0; }
  body.sidebar-collapsed .side-link .lock,
  body.sidebar-collapsed .side-link .dot-pend { display: none; }
  /* Con la barra compacta el contador ya no cabe a la derecha del texto (no hay texto): se SOBREPONE
     al ícono, con el MISMO formato y el MISMO solape que la chapa de la campana del topbar
     (.icon-btn .dot). En flujo normal lleva `margin-left:auto` y se iba al borde derecho del enlace,
     lejos del ícono.

     ⚠️ La clase es `.nav-badge` (la que crea updateSidebarBadges / updateMsgBadge / el aviso de
     Nubia). NO existe ningún `.count` en el portal: una regla escrita contra `.count` no pinta nada
     y no da ningún error — se probó dos veces y se subió dos veces sin efecto (28-jul-2026).

     Va colgada de la ESQUINA superior derecha del enlace (su recuadro redondeado), no centrada sobre
     el ícono: mismos -5px que usa la campana respecto a su botón. Con un número de dos cifras crece
     hacia la izquierda, así que nunca se sale de la barra. */
  body.sidebar-collapsed .sidebar .side-link { position: relative; }
  body.sidebar-collapsed .side-link .nav-badge {
    position: absolute; top: -5px; right: -5px; left: auto; margin: 0; transform: none;
    min-width: 19px; height: 19px; padding: 0 5px; border-radius: 100px;
    background: var(--accent); color: #fff; border: 2px solid var(--paper);
    font-size: .68rem !important; font-weight: 700; line-height: 1;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 2px 5px rgba(28,26,24,.35); }
  /* El aro de la chapa se queda BLANCO también en la sección activa (la del degradado negro-coral).
     Antes se teñía de --ink para "fundirse" con el fondo oscuro, y el resultado era que la chapa
     perdía su aro justo donde más falta hace para despegarse del coral del degradado. */
  body.sidebar-collapsed .side-profile { justify-content: center; padding-left: 0; padding-right: 0; }
  body.sidebar-collapsed .side-profile .meta { display: none; }
}
/* Grillas de 2 columnas del CONTENIDO: el breakpoint de arriba (980px) es del VIEWPORT completo,
   pero la sidebar fija (264px, ver .app-main) le resta ese ancho al contenido real — a 1024px de
   viewport solo quedan ~760px de contenido, más angosto de lo que estas grillas "desktop" esperan
   (caso confirmado en el audit de responsividad, R11.2). Se activan recién cuando el contenido
   efectivo (viewport - 264px de sidebar) alcanza los ~980px que este diseño siempre asumió. */
@media (min-width: 1240px) {
  .feed-layout { grid-template-columns: 1fr 320px; }
  .profile-layout { grid-template-columns: 1.7fr 1fr; }
  .plan-overview { grid-template-columns: 1fr auto; }
  .topbar-search-wrap { display: block; }
}
@media (max-width: 979px) {
  .sidebar { transform: translateX(-100%); box-shadow: 0 0 60px rgba(28,26,24,.2); z-index: 80; }
  body.drawer-open .sidebar { transform: none; }
  .app-main { margin-left: 0; }
  .topbar-burger { display: grid; }
}
/* Pulido fino de móvil (≤560px): en pantallas angostas los huecos generosos de escritorio
   ahogan el título de cada tarea/lección (queda en ~73px y rompe en 4 líneas). Se comprimen
   solo los huecos y el padding lateral — sin tocar el tamaño de letra ni el layout de escritorio.
   Además se agrega espacio inferior al contenido para que los botones flotantes (chat + Nubia,
   position:fixed) no tapen la última fila de acciones. */
@media (max-width: 560px) {
  .app-content .task-head, .app-content .lesson { gap: .55rem; padding-left: .5rem; padding-right: .5rem; }
  /* Margen lateral del contenido más ajustado en móvil (pedido cliente 17-jul): de ~21px (el mínimo del
     clamp base) a .85rem (~13.6px). Aplica a TODO el contenido del área de miembros (feed, módulos,
     etc.), así que las publicaciones quedan más cerca de los bordes y con más ancho útil. */
  .app-content { padding-left: .85rem; padding-right: .85rem; padding-bottom: 84px; }
  /* La miniatura de una publicación se encoge pero NO baja debajo del texto: así lo primero que se lee
     sigue siendo el mensaje. A 375px deja ~200px de ancho útil para el texto. */
  .fp-cols { grid-template-columns: minmax(0, 1fr) 96px; gap: .8rem; }
  /* El cuerpo va justificado en toda la app, pero en una columna de ~200px el justificado abre huecos
     enormes entre palabras («Hola   a   todos,   les»). Solo dentro de la rejilla y solo en móvil. */
  .fp-cols .fp-body, .fp-cols .fp-body-rich, .fp-cols .fp-body-rich p { text-align: left; }
  .iv-nav { width: 36px; height: 36px; }
  .iv-prev { left: .5rem; } .iv-next { right: .5rem; }

  /* ===== Compactación móvil del área de miembros (auditoría 17-jul-2026) =====
     Medido a 375px. Solo móvil; escritorio queda igual. */
  /* Botones: 27px → 16px de relleno lateral. Era el problema REAL medido: una fila de dos botones en
     un modal ("Cancelar" + "Guardar cambios") pedía 285px y solo había 258px, así que el segundo caía
     a otra línea. Esto libera 44px por fila y pasa a sobrar espacio. La altura no cambia (se toca igual). */
  .app-body .btn { --pad-x: 1rem; }
  /* --card-pad alimenta el relleno lateral de composer, prog-intro, plan-overview, gantt y métricas:
     bajándolo acá se compactan todas de una sola vez. */
  .app-body { --card-pad: 1rem; }                                   /* 21px → 16px */
  .feed-post, .rail-card { padding-left: 1rem; padding-right: 1rem; } /* usan 1.3rem fijo, no la variable */
  /* Ojo: .app-modal-card y .gate-card NO se pueden compactar acá — sus reglas base están MÁS ABAJO en
     este archivo (líneas ~632 y ~832) y con igual especificidad gana la última. Van al final del archivo. */
  /* Fila de evento (Próximas sesiones): en escritorio va [fecha][info][Unirme] en una línea, pero
     en móvil el botón deja el detalle en ~69px y parte la hora. Se hace que el detalle ocupe el
     ancho completo (fecha aparte) y el botón baje a su propia línea, alineado bajo el detalle. */
  .evt-item { flex-wrap: wrap; }
  .evt-item .evt-info { flex-basis: calc(100% - 70px); }
  .evt-item .evt-join { margin-left: 70px; margin-top: .3rem; }
  /* Barra de escribir mensaje (chat): en desktop entran 3 tools + input + "Enviar" en una línea,
     pero en 333px el input (con min-width intrínseco) no encoge y empuja "Enviar" fuera del box.
     Se deja que el input encoja (min-width:0) y se ajustan tools/padding para darle aire. */
  .conv-form input[type="text"], .conv-form textarea { min-width: 0; }
  .msg-conv .conv-form { padding-left: .65rem; padding-right: .65rem; gap: .4rem; }
  .conv-form .conv-tool { width: 36px; height: 36px; }
  .conv-form .btn--coral { padding-left: .95rem; padding-right: .95rem; }
}
@media (prefers-reduced-motion: reduce) {
  .view { animation: none; }
  .bar-col .bar, .module-bar i, .ring, .donut { transition: none; }
}

/* =============================================================
   12. AUTH / PERMISOS — sidebar foot, gates, comunidades,
       miembros, mensajería, modal
   ============================================================= */
.side-role, [data-role-badge] { display: inline; }
.btn-xs { --pad-y: .5rem; --pad-x: .9rem; font-size: .8rem; }
.gate-card { padding: clamp(2rem,5vw,3rem); text-align: center; max-width: 560px; margin: 0 auto; }
.gate-ico { width: 60px; height: 60px; margin: 0 auto 1.2rem; border-radius: 50%; background: var(--bg); border: 1px solid var(--line-2); display: grid; place-items: center; color: var(--ink-mute); }
.gate-ico .app-icon { width: 24px; height: 24px; }
.gate-card h2 { font-family: var(--serif); font-weight: 300; font-size: 1.7rem; }
.gate-card p { color: var(--ink-soft); margin: .8rem auto 0; max-width: 42ch; }
/* Cronograma finalizado / no disponible (regla de fecha de término y toggle del equipo). */
.cron-ended .gate-ico { color: var(--accent-deep); background: var(--accent-soft); border-color: transparent; }
.cron-ended-cta { color: var(--accent-deep) !important; font-weight: 500; }
.gate-meta { display: flex; flex-wrap: wrap; gap: .5rem; justify-content: center; margin: 1.4rem 0; }
.gate-meta li { font-size: .8rem; background: var(--bg); border: 1px solid var(--line-2); border-radius: 100px; padding: .35rem .9rem; color: var(--ink-soft); }
.gate-card .btn { margin-top: .4rem; }
/* Mensaje del box "+" (modal) */
.plus-msg { text-align: center; max-width: 440px; padding: .4rem .6rem .8rem; }
.plus-msg .gate-ico { margin: 0 auto 1rem; }
.plus-msg h2 { font-family: var(--serif); font-weight: 300; font-size: 1.45rem; }
.plus-msg p { color: var(--ink-soft); margin-top: .7rem; }
/* ---- Placeholder ANIMADO de comunidad (miembro sin acceso) · se reemplaza por el video/animación del cliente ---- */
.comm-soon { text-align: center; padding: clamp(2rem,5vw,3.5rem); max-width: 640px; margin: 0 auto; overflow: hidden; }
.comm-soon-stage { position: relative; width: 280px; height: 200px; margin: 0 auto 1.6rem; }
.cs-core { position: absolute; left: 50%; top: 50%; width: 58px; height: 58px; margin: -29px 0 0 -29px; border-radius: 50%; background: var(--grad-portada); animation: csCore 3.2s ease-in-out infinite; }
@keyframes csCore { 0%,100% { transform: scale(1); box-shadow: 0 0 24px 4px rgba(242,107,94,.32); } 50% { transform: scale(1.12); box-shadow: 0 0 42px 12px rgba(242,107,94,.16); } }
.cs-ring { position: absolute; left: 50%; top: 50%; width: 78px; height: 78px; margin: -39px 0 0 -39px; border-radius: 50%; border: 1.5px solid rgba(242,107,94,.5); animation: csRing 3.6s ease-out infinite; }
.cs-ring2 { animation-delay: 1.8s; }
@keyframes csRing { 0% { transform: scale(.55); opacity: .85; } 100% { transform: scale(2.6); opacity: 0; } }
.cs-bubble { position: absolute; border-radius: 50%; display: grid; place-items: center; font-weight: 600; font-size: .82rem; background: var(--accent-soft); color: var(--accent-deep); box-shadow: 0 6px 16px rgba(28,26,24,.12); }
.cs-b1 { width: 44px; height: 44px; left: 11%; top: 13%; animation: csFloat 5s ease-in-out infinite; }
.cs-b1--photo { background-size: cover; background-position: center top; background-color: var(--paper); border: 2px solid var(--paper); }
.cs-b2 { width: 34px; height: 34px; right: 13%; top: 7%; animation: csFloat 6s ease-in-out infinite .5s; }
.cs-b3 { width: 40px; height: 40px; right: 7%; bottom: 15%; animation: csFloat 5.5s ease-in-out infinite 1s; }
.cs-b4 { width: 22px; height: 22px; left: 7%; bottom: 21%; animation: csFloat 4.6s ease-in-out infinite .8s; }
.cs-b5 { width: 15px; height: 15px; left: 41%; top: 3%; animation: csFloat 5.2s ease-in-out infinite .3s; }
.cs-b6 { width: 18px; height: 18px; right: 33%; bottom: 5%; animation: csFloat 4.8s ease-in-out infinite 1.3s; }
@keyframes csFloat { 0%,100% { transform: translate(0,0); } 33% { transform: translate(4px,-10px); } 66% { transform: translate(-5px,6px); } }
.comm-soon h2 { font-family: var(--serif); font-weight: 300; font-size: 1.6rem; }
.comm-soon p { color: var(--ink-soft); margin: .7rem auto 0; max-width: 44ch; }
@media (prefers-reduced-motion: reduce) { .cs-core, .cs-ring, .cs-bubble { animation: none; } }
/* ---- Calendario bloqueado (alumno sin plan): mes real + cursor que hace clic/zoom en 6 fechas ----
   El card lleva además la clase .comm-soon → hereda centro, ancho, padding y estilos h2/p (mismo box que Comunidad). */
/* Desde el 11-ago-2026 .cal-layout es de UNA columna; el `1 / -1` se deja porque no estorba y
   mantiene centrada la tarjeta de «sin acceso» si algún día vuelve a haber más de una. */
.cal-layout > .cal-locked { grid-column: 1 / -1; }
.cal-stage { position: relative; width: min(320px,100%); height: 200px; margin: 0 auto 1.6rem; }
.cal-mini { position: absolute; inset: 0; }
.cm-head { font-size: .82rem; font-weight: 600; color: var(--ink); text-align: center; margin-bottom: .3rem; }
.cm-dows { display: grid; grid-template-columns: repeat(7,minmax(0,1fr)); margin-bottom: .18rem; }
.cm-dow { font-size: .58rem; color: var(--ink-mute); text-align: center; }
.cm-grid { display: grid; grid-template-columns: repeat(7,minmax(0,1fr)); gap: 3px; }
.cm-cell { position: relative; height: 23px; display: grid; place-items: center; font-size: .7rem; color: var(--ink-soft); border-radius: 6px; transition: transform .3s ease, background .3s, color .3s, box-shadow .3s; }
.cm-empty { visibility: hidden; }
.cm-evt { font-weight: 600; color: var(--ink); background: var(--bg); }
.cm-evt::after { content: ""; position: absolute; bottom: 2.5px; left: 50%; transform: translateX(-50%); width: 4px; height: 4px; border-radius: 50%; background: var(--accent); }
.cm-evt.t-vent::after { background: #1f7a4d; }
.cm-evt.t-neuro::after { background: #7c5cff; }
.cm-evt.t-ment::after { background: #F26B5E; }
.cm-evt.t-tech::after { background: #3b82c4; }
.cm-evt.t-work::after { background: #EE8B76; }
.cm-evt.t-pres::after { background: #e0a93b; }
.cm-cell.is-marked { background: var(--ink); color: #fff; }
.cm-cell.is-marked::after { background: #fff; }
.cal-cursor { position: absolute; top: 0; left: 0; margin: -3px 0 0 -4px; z-index: 6; pointer-events: none; transition: transform .6s cubic-bezier(.45,.05,.25,1); filter: drop-shadow(0 2px 3px rgba(0,0,0,.25)); }
.cal-cursor svg { display: block; transition: transform .12s ease; }
.cal-cursor.is-click svg { transform: scale(.78); }
.cal-pop { position: absolute; width: 150px; padding: .42rem .6rem; border-radius: 10px; background: var(--ink); color: var(--bg); box-shadow: 0 10px 24px rgba(28,26,24,.32); opacity: 0; transform: translateY(5px) scale(.96); transition: opacity .25s, transform .25s; z-index: 7; pointer-events: none; text-align: left; }
.cal-pop.is-show { opacity: 1; transform: translateY(0) scale(1); }
.cal-pop-name { display: block; font-size: .76rem; font-weight: 600; line-height: 1.25; }
.cal-pop-date { display: block; font-size: .66rem; opacity: .82; margin-top: 1px; }
@media (prefers-reduced-motion: reduce) { .cal-cursor { transition: none; } }

/* Cronograma bloqueado (alumno sin plan): línea de tiempo animada con el viaje del embudo
   (propuesta → redes → anuncios → leads → cierre). Mismo tratamiento de tarjeta/h2/p que
   .comm-soon (hereda centro, padding, tipografía) — solo el ancho máximo crece para que quepan
   las 5 estaciones con buen espaciado. */
.cron-locked.comm-soon { max-width: 900px; }
.cron-stage { position: relative; width: 100%; padding-top: 50px; margin: 0 auto 2.6rem; }
.cron-timeline { display: flex; justify-content: space-between; align-items: flex-start; position: relative; padding: 0 24px; }
.cron-seg { position: absolute; height: 3px; border-radius: 100px; background: var(--bg-3); overflow: hidden; }
.cron-seg-fill { position: absolute; inset: 0; border-radius: 100px; background: var(--grad-box); transform: scaleX(0); transform-origin: left center; }
.cron-seg-fill.is-animating { transition: transform 1.15s var(--ease-out); }
.cron-seg-fill.is-filled { transform: scaleX(1); }
.cron-node { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; width: 70px; }
.cron-node-dot { width: 24px; height: 24px; border-radius: 50%; background: var(--bg-3); display: grid; place-items: center; transition: transform .55s var(--ease-out), background .5s; }
.cron-node.is-done .cron-node-dot { background: var(--grad-box); }
.cron-node.is-active .cron-node-dot { background: var(--grad-box); transform: scale(1.55); }
/* Icono fijo en el centro del punto: silueta negra sobre fondo blanco, siempre visible (no es
   parte del efecto — lo que aparece y desaparece es solo el texto de abajo). display:flex (en vez
   de dejar el <svg> como inline) evita el hueco de línea base que lo corre del centro. */
.cron-node-dot-icon { width: 18px; height: 18px; border-radius: 50%; background: var(--paper); display: flex; align-items: center; justify-content: center; }
.cron-node-dot-icon svg { display: block; width: 13px; height: 13px; stroke: var(--ink); overflow: visible; }
/* Candado de la última estación: el gancho arranca entreabierto y "se cierra" (gira a su posición
   de reposo) recién cuando termina el zoom del círculo (delay = duración del zoom, .55s) —
   representa el cierre de venta. Vuelve a abrirse solo al reiniciar la vuelta. */
.lock-shackle { transform-origin: 8px 10.5px; transform: rotate(-14deg); transition: transform .5s var(--ease-out) .55s; }
.cron-node.is-active .lock-shackle, .cron-node.is-done .lock-shackle { transform: rotate(0deg); }
/* Efecto al llegar a un punto: solo texto, con chispas que salen disparadas alrededor — nada queda
   pegado como un cartel, todo se desvanece solo. */
.cron-burst { position: absolute; bottom: 100%; left: 50%; width: 0; height: 0; margin-bottom: 14px; pointer-events: none; }
.cron-spark { position: absolute; top: 0; left: 0; width: 6px; height: 6px; margin: -3px 0 0 -3px; border-radius: 50%; background: var(--accent); opacity: 0; }
.cron-label-burst { position: absolute; top: 0; left: 0; transform: translate(-50%, -100%) scale(.7); white-space: nowrap; font-size: .78rem; font-weight: 300; color: var(--ink); opacity: 0; }
.cron-burst.is-firing .cron-spark { animation: cronSparkFly .85s var(--ease-out) forwards; }
.cron-burst.is-firing .cron-label-burst { animation: cronLabelBurst .9s var(--ease-out) forwards; }
@keyframes cronLabelBurst {
  0%   { transform: translate(-50%, -100%) scale(.6); opacity: 0; }
  35%  { transform: translate(-50%, -128%) scale(1); opacity: 1; }
  75%  { transform: translate(-50%, -136%) scale(1); opacity: 1; }
  100% { transform: translate(-50%, -152%) scale(.92); opacity: 0; }
}
@keyframes cronSparkFly {
  0%   { transform: translate(0,0) scale(1); opacity: 1; }
  100% { transform: translate(var(--dx), var(--dy)) scale(.3); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .cron-seg-fill, .cron-node-dot, .lock-shackle { transition: none !important; }
  .cron-burst.is-firing .cron-spark, .cron-burst.is-firing .cron-label-burst { animation: none !important; }
}

/* Lista de comunidades */
.communities-grid, .comm-list { display: grid; gap: 1rem; }

/* Miembros */
.members-toolbar { display: flex; flex-wrap: wrap; gap: .8rem; align-items: center; justify-content: space-between; margin-bottom: 1.4rem; }
.members-search { display: flex; align-items: center; gap: .5rem; background: var(--paper); border: 1px solid var(--line); border-radius: 100px; padding: .6rem 1rem; flex: 1; min-width: 240px; color: var(--ink-mute); }
.members-search input { border: 0; background: none; outline: none; font: inherit; font-size: .9rem; width: 100%; color: var(--ink); }
.members-filters { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }
/* Filtros como desplegables (categoría + plan) */
.members-select { display: inline-flex; align-items: center; gap: .5rem; background: var(--paper); border: 1px solid var(--line); border-radius: 100px; padding: .3rem .55rem .3rem .95rem; transition: border-color .2s; }
.members-select:hover { border-color: var(--ink-mute); }
/* Mismo estilo de texto que el buscador de la izquierda (.members-search input): sin mayúsculas
   forzadas, sin negrita ni tracking extra — antes se veían "más pesadas" que el buscador. */
.members-select-lbl { font: inherit; font-size: .9rem; color: var(--ink-mute); font-weight: 400; }
.members-select select { appearance: none; -webkit-appearance: none; border: 0; background: none; outline: none; font: inherit; font-size: .9rem; font-weight: 400; color: var(--ink); cursor: pointer; padding: .15rem 1.3rem .15rem .15rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237a736b' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right .25rem center; background-size: 14px; }
.members-banner { display: flex; align-items: center; gap: .6rem; background: var(--accent-soft); color: var(--accent-deep); border-radius: 12px; padding: .8rem var(--card-pad); margin-bottom: 1.2rem; font-size: .9rem; }
.members-banner b { font-weight: 700; }
/* Aviso "viendo la programación de X" (coach/admin entrando desde Seguimiento de alumnos) — mismo
   degradado negro→coral que los elementos activos del sidebar (.side-link.is-active). */
.prog-viewing-banner { display: flex; align-items: center; justify-content: space-between; gap: .8rem; flex-wrap: wrap; background: var(--grad-box); color: var(--paper); border-radius: 12px; padding: .8rem var(--card-pad); margin-bottom: 1.2rem; font-size: .9rem; }
.prog-viewing-banner b { font-weight: 700; }
.prog-viewing-banner .app-icon { stroke: var(--paper); }
.prog-viewing-banner .btn--ghost { border-color: rgba(244,241,234,.4); color: var(--paper); background: transparent; }
.prog-viewing-banner .btn--ghost:hover { background: rgba(244,241,234,.16); border-color: rgba(244,241,234,.65); color: var(--paper); }
/* Tarjeta de "Solicitar/gestionar pausa del programa" en #programacion (R4.7, R7) */
.prog-pause-card { padding: 1.2rem var(--card-pad); margin-bottom: 1.2rem; }
.prog-pause-card h3 { display: flex; align-items: center; gap: .4rem; font-size: .95rem; margin: 0 0 .8rem; }
.prog-pause-card .field { max-width: 220px; margin-bottom: .8rem; }
.prog-pause-card .field:has(textarea) { max-width: 50%; }
.prog-pause-actions { display: flex; gap: .6rem; }
/* Atajo "¿Necesitas una pausa?" al pie del cronograma → abre la pausa en pestaña emergente (modal) */
.prog-pause-cta { display: flex; justify-content: center; margin-top: 1.3rem; }
/* Línea de tiempo de una tarea (2-ago-2026). Reutiliza .pause-hist / .ph-item de la historia de
   pausas; acá solo se ajusta la escala, porque va dentro de una tarea y no en una tarjeta propia. */
.tarea-hist { margin: .9rem 0 .2rem; padding-left: 1.15rem; }
.tarea-hist .ph-item { padding-bottom: .6rem; }
.tarea-hist .ph-item::before { left: -1.15rem; top: .3rem; width: 9px; height: 9px; }
.tarea-hist .ph-head b { font-size: .8rem; }
.tarea-hist .ph-head time { font-size: .7rem; }
.tarea-hist .ph-item p { font-size: .76rem; margin-top: .1rem; }
.tarea-hist .ph-item.is-off .ph-head b { color: var(--ink-mute); }

/* ---------- Correos · edición del texto (2-ago-2026) ----------
   Dos columnas: la lista de los ocho a la izquierda, el editor a la derecha. En móvil se apilan.
   El aviso de error usa el coral profundo, no rojo: es una corrección, no una catástrofe. */
.correos-intro { padding: 1.1rem var(--card-pad); margin-bottom: 1rem; }
.correos-intro p { margin: 0 0 .4rem; font-size: .92rem; line-height: 1.55; }
.correos-intro p:last-child { margin-bottom: 0; }

.correos-split { display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 2fr); gap: 1rem; align-items: start; }
.correos-lista { padding: .5rem; display: grid; gap: .2rem; }
.correo-item { display: grid; gap: .1rem; text-align: left; padding: .6rem .7rem; border-radius: 12px;
  background: transparent; border: 0; cursor: pointer; transition: background .18s; }
.correo-item:hover { background: var(--bg-2); }
.correo-item.is-active { background: var(--bg-3); }
.correo-nom { font-size: .9rem; font-weight: 600; color: var(--ink); }
.correo-desc { font-size: .74rem; color: var(--ink-mute); line-height: 1.35; }

.correos-edit { padding: 1.2rem var(--card-pad); }
.correos-edit h3 { margin: 0 0 .15rem; font-size: 1rem; }
.correos-edit .field { margin-bottom: .9rem; }
.correos-edit textarea { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .84rem; line-height: 1.6; }

.correos-vars { border-top: 1px solid var(--line-2); padding-top: .9rem; margin-top: .3rem; }
.correos-vars-t { font-size: .8rem; font-weight: 600; margin: 0 0 .5rem; }
.correos-chips { display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: .5rem; }
.correo-chip { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .76rem;
  padding: .25rem .55rem; border-radius: 100px; border: 1px solid var(--line);
  background: var(--paper); color: var(--ink-soft); cursor: pointer; transition: all .18s; }
.correo-chip:hover { border-color: var(--accent); color: var(--accent-deep); }

.correos-ayuda { margin: .9rem 0 .2rem; font-size: .84rem; }
.correos-ayuda summary { cursor: pointer; color: var(--ink-soft); font-weight: 600; }
.correos-ayuda ul { margin: .6rem 0 .4rem; padding-left: 1.1rem; display: grid; gap: .25rem; color: var(--ink-soft); }
.correos-ayuda code { font-size: .82rem; background: var(--bg-2); padding: .05rem .3rem; border-radius: 5px; }

.correos-msg { font-size: .84rem; margin: .8rem 0 0; color: var(--ink-soft); }
.correos-msg.is-error { color: var(--accent-deep); }
.correos-acciones { display: flex; gap: .6rem; margin-top: .9rem; }

@media (max-width: 860px) {
  .correos-split { grid-template-columns: minmax(0, 1fr); }
}

/* Tope de pausa (2-ago-2026). Dos piezas: el aviso de que ya no queda saldo —que SUSTITUYE al
   formulario, no lo acompaña— y la línea de saldo cuando todavía se puede pedir.
   Sin color de alarma a propósito: no es un error del alumno, es una regla del programa. */
.pausa-tope { border: 1px solid var(--line); border-radius: 14px; padding: 1rem 1.1rem; background: var(--paper); }
.pausa-tope p { margin: 0 0 .5rem; font-size: .88rem; color: var(--ink-soft); line-height: 1.5; }
.pausa-tope p:last-child { margin-bottom: 0; }
.pausa-tope-t { font-weight: 700; color: var(--ink) !important; font-size: .95rem !important; }
.pausa-saldo { border-left: 3px solid var(--coral); padding: .1rem 0 .1rem .7rem; margin: 0 0 .8rem;
  font-size: .85rem; color: var(--ink-soft); line-height: 1.45; }

/* Dentro del modal la tarjeta no lleva su propio padding/margen (el modal ya lo aporta). */
.app-modal-card .prog-pause-card { padding: 0; margin: 0; }
.app-modal-card .prog-pause-card .field:has(textarea) { max-width: 100%; }

.members-grid { display: grid; grid-template-columns: 1fr; gap: 1rem; align-items: stretch; }
/* Grupos dentro de "Todos": Equipo / Miembros, con subtítulo previo a las tarjetas */
.members-group + .members-group { margin-top: 1.9rem; }
.members-group-label { margin: 0 0 .9rem; font: 600 .78rem/1 var(--sans); color: var(--ink-mute); }
.member-card { padding: 1.2rem 1.3rem; display: flex; flex-direction: column; gap: .8rem; cursor: pointer; transition: transform .22s var(--ease-out), box-shadow .22s; }
.member-card:hover { transform: translateY(-3px); box-shadow: 0 18px 40px -24px rgba(28,26,24,.45); }
.member-top { display: flex; align-items: center; gap: .8rem; }
.member-id { flex: 1; min-width: 0; }
.member-id b { display: block; font-size: .95rem; font-family: var(--serif); }
.member-id span { font-size: .78rem; color: var(--ink-mute); }
/* Alto fijo (2 líneas) para que todas las tarjetas midan lo mismo sin importar el largo de la bio;
   el texto completo sigue disponible en un clic (avatar/nombre abren el perfil completo). */
/* La biografía de la tarjeta: se recorta a 2 líneas con puntos suspensivos, pero SIN partir
   palabras (llevaba break-all y salía "en la p / agina"). Ver la regla general de arriba. */
.member-meta { font-size: .82rem; color: var(--ink-mute); overflow-wrap: break-word; word-break: normal; line-height: 1.35; min-height: 2.7em; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.member-tags { display: flex; flex-wrap: wrap; gap: .4rem; }
/* "Declaró ser miembro activo": sin caja/pill — solo ícono + texto coral en UNA sola línea. El
   font-size lleva !important para ganarle a la regla global de 14px del área de miembros y que entre. */
.member-declared { display: inline-flex; align-items: center; gap: .35rem; color: var(--accent-deep); font-weight: 600; font-size: 13px !important; white-space: nowrap; margin: .15rem 0 .35rem; }
/* Aviso de posible duplicado en una solicitud pendiente (3-ago-2026). Comparte el aire de
   `.member-declared` pero en rojo: no es un dato de la persona, es algo que hay que mirar. */
.member-dup { display: flex; gap: .45rem; align-items: flex-start; margin: .5rem 0 0;
  padding: .5rem .6rem; border-radius: 12px; background: rgba(242, 107, 94, .09);
  border: 1px solid rgba(242, 107, 94, .28); color: var(--accent-deep);
  font-size: .78rem; line-height: 1.45; }
.member-dup .app-icon { width: 14px; height: 14px; flex: 0 0 auto; margin-top: .12rem; }
.member-dup b { font-weight: 600; }
/* Correo rebotado (fase2/106): mismo formato que el aviso de duplicado, pero en ámbar. Se distingue
   a propósito — el duplicado es «míralo antes de decidir»; este es «esta persona NO puede entrar». */
.member-bounce { background: rgba(191, 138, 32, .10); border-color: rgba(191, 138, 32, .32); color: #7a5410; }
/* Correo CONFIRMADO (fase2/107). Verde, y es el único aviso de la tarjeta que da una buena noticia:
   los otros tres piden mirar algo. */
.member-ok { background: rgba(47, 111, 79, .09); border-color: rgba(47, 111, 79, .26); color: #2c5c42; }
.member-declared .app-icon { width: 15px; height: 15px; }
.m-tag { font-size: .72rem; background: var(--accent-soft); color: var(--accent-deep); border-radius: 100px; padding: .2rem .7rem; font-weight: 600; }
.m-tag.empty { background: var(--bg); color: var(--ink-mute); font-weight: 400; }
.m-status { font-size: .6rem; padding: .18rem .5rem; border-radius: 100px; font-weight: 600; flex: 0 0 auto; }
.m-status.active { background: #e2f4ea; color: #1f7a4d; }
.m-status.admin { background: var(--ink); color: var(--bg); }
.m-status.risk { background: #f7ecd6; color: #8a5a00; }
.member-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.member-actions .app-icon { width: 14px; height: 14px; }
.m-note { font-size: .76rem; color: var(--ink-mute); font-style: italic; }

/* Tarjeta de admin / CEO: fondo degradado negro→coral (Portada) */
.member-card--dark { background: var(--grad-portada); border-color: transparent; color: var(--paper); }
.member-card--dark .member-id b { color: var(--paper); }
.member-card--dark .member-id span,
.member-card--dark .member-meta,
.member-card--dark .member-coach { color: rgba(244,241,234,.78); }
.member-card--dark .member-coach .app-icon { color: rgba(244,241,234,.9); }
.member-card--dark .side-avatar { background: rgba(244,241,234,.2); color: var(--paper); }
.member-card--dark .m-status.admin { background: var(--paper); color: var(--ink); }
.member-card--dark .m-status.team { background: var(--paper); color: #5b46a8; }
.member-card--dark .m-tag { background: rgba(244,241,234,.16); color: var(--paper); }
.member-card--dark .m-tag.empty { background: rgba(244,241,234,.1); color: rgba(244,241,234,.7); }
.member-card--dark .btn--ghost { border-color: rgba(244,241,234,.38); color: var(--paper); background: transparent; }
.member-card--dark .btn--ghost:hover { background: rgba(244,241,234,.14); border-color: rgba(244,241,234,.65); }
.member-card--dark .m-note { color: rgba(244,241,234,.62); }
.member-card--dark .lnk:hover { color: #fff; opacity: 1; }

/* Modal */
.app-modal { position: fixed; inset: 0; z-index: 200; display: grid; place-items: center; padding: 1.2rem; }
.app-modal[hidden] { display: none; }
.app-modal-backdrop { position: absolute; inset: 0; background: rgba(28,26,24,.45); backdrop-filter: blur(3px); }
/* Tamaño de las pestañas emergentes (28-jul-2026, pedido del cliente): ancho 440 → 560 (+27%) y alto
   máximo 86 → 90vh. El ancho sube bastante porque es lo que evita el scroll — una descripción larga o
   una lista de miembros caben mucho mejor en 560 que en 440; el alto sube poco a propósito, para no
   pegar la tarjeta a los bordes de la pantalla. */
.app-modal-card { position: relative; background: var(--paper); border-radius: var(--radius-lg); padding: 1.6rem; width: 100%; max-width: 560px; max-height: 90vh; overflow-y: auto; box-shadow: 0 40px 90px -30px rgba(28,26,24,.5); animation: viewIn .35s var(--ease-out); }
/* Excepciones que NO crecen: son piezas cortas donde el ancho extra solo dejaría aire vacío.
   Se detectan por su contenido (`:has`), sin tocar el JS que las abre.
   · .profile-modal → la ficha compacta de miembro, cuyo diseño el cliente aprobó tal cual.
   · .plus-msg / .gate-card → avisos de "sin acceso": un título y dos líneas. */
.app-modal-card:has(> .profile-modal), .app-modal-card:has(> .plus-msg), .app-modal-card:has(> .gate-card) { max-width: 440px; }
.manage-head { display: flex; align-items: center; gap: .9rem; padding-bottom: 1.2rem; border-bottom: 1px solid var(--line-2); margin-bottom: 1rem; }
.manage-head h3 { font-family: var(--serif); font-weight: 400; font-size: 1.3rem; }
.manage-head span { font-size: .78rem; color: var(--ink-mute); }
/* Los tres datos del miembro en una línea, con su icono y aire entre ellos (opción A del cliente).
   ⚠️ Los selectores llevan `.manage-head` delante A PROPÓSITO: la regla de arriba pinta CUALQUIER
   `span` de la cabecera y es más específica que una clase suelta, así que sin ese prefijo el color
   del consultor no se aplicaría — y no se vería el fallo, porque el gris es un color creíble. */
.manage-head .mh-info { min-width: 0; }
.manage-head .mh-datos { display: flex; align-items: center; flex-wrap: wrap; gap: .4rem 2.2rem; margin-top: .45rem; }
.manage-head .mh-dato { display: inline-flex; align-items: center; gap: .38rem; font-size: .78rem; color: var(--ink-mute); white-space: nowrap; }
.manage-head .mh-dato .app-icon { width: 13px; height: 13px; flex: 0 0 auto; opacity: .75; }
.manage-head .mh-dato--coach, .manage-head .mh-dato--coach span { color: var(--accent); }
.manage-head .mh-dato--coach b { color: var(--accent-deep); font-weight: 600; }
/* En pantalla estrecha el correo largo tiene que poder cortarse en vez de empujar la tarjeta. */
@media (max-width: 560px) {
  .manage-head .mh-datos { gap: .35rem .9rem; }
  .manage-head .mh-dato { white-space: normal; overflow-wrap: break-word; min-width: 0; }
}
.manage-section { margin-bottom: 1.2rem; }
.manage-section h4 { font-size: .72rem; color: var(--ink-mute); margin-bottom: .6rem; }
.manage-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: .5rem 0; font-size: .9rem; }
.manage-row + .manage-row { border-top: 1px solid var(--line-2); }
/* Select de "Coach asignado": mismos valores que .field select (app.css), pero sin envolverlo en
   .field (que es width:100% — rompería el layout flex de .manage-row, label a la izquierda). */
.manage-row select { padding: .5rem 2.3rem .5rem .95rem; border-radius: 100px; border: 1px solid var(--line); background: var(--bg); font: inherit; font-size: .9rem; color: var(--ink); cursor: pointer; min-width: 200px; transition: border-color .25s, background .25s; appearance: none; -webkit-appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237a736b' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 1rem center; background-size: 13px; }
.manage-row select:focus { outline: none; border-color: var(--accent); background: var(--paper); }

/* ============================================================================
   Desplegable a medida (.mps) y selector de fecha (.mpd) — reemplazan el look
   nativo de <select> / <input type=date>. El control nativo queda oculto (fuente
   de la verdad); el botón + lista los dibuja el componente (ver mpsEnhance/mpdEnhance).
   ============================================================================ */
.mps { position: relative; }
.mps > select { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.mps-btn { width: 100%; display: flex; align-items: center; gap: .6rem; text-align: left; font: inherit; color: var(--ink);
  background: var(--bg); border: 1px solid var(--line); border-radius: 100px; padding: .6rem 1rem; cursor: pointer;
  transition: border-color .2s, background .2s, box-shadow .2s; }
.mps-btn:hover { border-color: var(--ink-mute); }
.mps.is-open > .mps-btn { border-color: var(--accent); background: var(--paper); box-shadow: 0 0 0 3px var(--accent-glow); }
.mps.is-disabled > .mps-btn { opacity: .55; cursor: not-allowed; }
.mps-val { flex: 1; min-width: 0; display: flex; align-items: center; gap: .6rem; font-size: .9rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mps-val.is-rich .mps-txt { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.mps-val.is-rich .mps-txt b { font-weight: 600; font-size: .88rem; overflow: hidden; text-overflow: ellipsis; }
.mps-val.is-rich .mps-txt small { font-size: .72rem; color: var(--ink-mute); }
.mps-chev { width: 15px; height: 15px; flex: 0 0 auto; color: var(--ink-mute); transition: transform .22s; }
.mps.is-open > .mps-btn .mps-chev { transform: rotate(180deg); }
.mps-list { position: absolute; left: 0; right: auto; top: calc(100% + 6px); z-index: 40; background: var(--paper);
  border: 1px solid var(--line); border-radius: 16px; box-shadow: 0 24px 60px -22px rgba(28,26,24,.5);
  padding: .35rem; max-height: 290px; overflow-y: auto; animation: mpsIn .16s ease-out;
  min-width: 100%; width: max-content; max-width: min(340px, calc(100vw - 24px)); }
.mps.mps-up > .mps-list { top: auto; bottom: calc(100% + 6px); }
.mps.mps-end > .mps-list { left: auto; right: 0; }
@keyframes mpsIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.mps-opt { display: flex; align-items: center; gap: .6rem; padding: .5rem .6rem; border-radius: 11px; cursor: pointer; font-size: .9rem; color: var(--ink-soft); transition: background .13s; white-space: nowrap; }
.mps-opt:hover, .mps-opt.is-hi { background: var(--bg-2); }
.mps-opt.is-sel { background: var(--accent-soft); color: var(--ink); }
.mps-opt.is-disabled { opacity: .5; cursor: not-allowed; }
.mps-opt .mps-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.2; }
.mps-opt.is-rich .mps-txt b { font-weight: 600; }
.mps-opt.is-rich .mps-txt small { font-size: .74rem; color: var(--ink-mute); }
.mps-opt:not(.is-rich) .mps-txt { flex-direction: row; align-items: center; }
.mps-check { width: 16px; height: 16px; flex: 0 0 auto; color: var(--accent-deep); opacity: 0; transition: opacity .13s; }
.mps-opt.is-sel .mps-check { opacity: 1; }
.mps-av { width: 34px; height: 34px; border-radius: 50%; flex: 0 0 auto; background: var(--grad-portada) center/cover no-repeat;
  display: grid; place-items: center; color: #fff; font-weight: 600; font-size: .85rem; }
.mps-val .mps-av { width: 26px; height: 26px; font-size: .74rem; }

/* Filtros ya envueltos en su propia píldora (.members-select) → el botón es transparente, la píldora
   la sigue dando el contenedor; solo cambia la lista abierta. */
.members-select .mps { display: inline-flex; }
.members-select .mps-btn { width: auto; border: 0; background: none; box-shadow: none; padding: .1rem 1.2rem .1rem .1rem; }
.members-select .mps.is-open > .mps-btn { border: 0; background: none; box-shadow: none; }
/* Filtros de métricas (.seg-filter) y "Coach asignado" (.manage-row): la píldora estaba en el propio
   <select>, así que el botón del componente la conserva, dimensionado igual. */
.seg-filter .mps-btn { width: auto; padding: .4rem 1.6rem .4rem .7rem; }
.manage-row .mps { min-width: 200px; }
.manage-row .mps-btn { padding: .5rem 1rem; }
/* Los FILTROS no van en negrita y con font un poco menor (las opciones caben en una línea gracias a
   width:max-content + white-space:nowrap de .mps-list/.mps-opt). No afecta a los selects de persona. */
.members-select .mps-btn, .seg-filter .mps-btn,
.members-select .mps-opt, .seg-filter .mps-opt { font-weight: 400; font-size: .85rem; }

/* Selector de fecha a medida (.mpd) */
.mpd { position: relative; }
.mpd > input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.mpd-btn { width: 100%; display: flex; align-items: center; gap: .6rem; text-align: left; font: inherit; font-size: .9rem; color: var(--ink);
  background: var(--bg); border: 1px solid var(--line); border-radius: 100px; padding: .6rem 1rem; cursor: pointer; transition: border-color .2s, background .2s, box-shadow .2s; }
.mpd-btn:hover { border-color: var(--ink-mute); }
.mpd.is-open > .mpd-btn { border-color: var(--accent); background: var(--paper); box-shadow: 0 0 0 3px var(--accent-glow); }
.mpd-btn.is-empty .mpd-lbl { color: var(--ink-mute); }
.mpd-btn svg { width: 16px; height: 16px; flex: 0 0 auto; color: var(--ink-mute); margin-left: auto; }
.mpd-cal { position: absolute; left: 0; top: calc(100% + 6px); z-index: 40; width: 290px; max-width: calc(100vw - 32px); background: var(--paper);
  border: 1px solid var(--line); border-radius: 18px; box-shadow: 0 24px 60px -22px rgba(28,26,24,.5); padding: .9rem; animation: mpsIn .16s ease-out; }
.mpd.mpd-up > .mpd-cal { top: auto; bottom: calc(100% + 6px); }
.mpd-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: .7rem; }
.mpd-title { font-weight: 600; font-size: .92rem; }
.mpd-nav { display: flex; gap: .3rem; }
.mpd-nav button { width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--line); background: var(--paper); color: var(--ink-soft); cursor: pointer; display: grid; place-items: center; transition: border-color .2s, background .2s, color .2s; }
.mpd-nav button:hover { border-color: var(--accent); color: var(--accent-deep); background: var(--accent-soft); }
.mpd-nav svg { width: 15px; height: 15px; }
.mpd-dow, .mpd-grid { display: grid; grid-template-columns: repeat(7, minmax(0,1fr)); gap: 2px; }
.mpd-dow span { text-align: center; font-size: .68rem; font-weight: 600; color: var(--ink-mute); padding: .25rem 0; }
.mpd-day { aspect-ratio: 1; border: 0; background: none; font: inherit; font-size: .82rem; color: var(--ink-soft); border-radius: 9px; cursor: pointer; transition: background .12s, color .12s; }
.mpd-day:hover { background: var(--bg-2); }
.mpd-day.is-out { color: var(--ink-mute); opacity: .5; }
.mpd-day.is-today { box-shadow: inset 0 0 0 1.5px var(--accent); }
.mpd-day.is-sel { background: linear-gradient(90deg, var(--accent), var(--accent-deep)); color: #fff; font-weight: 600; }
/* Día fuera del rango del campo (min/max): se ve, pero no se puede elegir. */
.mpd-day.is-off { opacity: .28; cursor: not-allowed; }
.mpd-day.is-off:hover { background: none; }
.mpd-foot button[disabled] { opacity: .4; cursor: not-allowed; }
.mpd-foot { display: flex; justify-content: space-between; margin-top: .7rem; padding-top: .6rem; border-top: 1px solid var(--line-2); }
.mpd-foot button { border: 0; background: none; font: inherit; font-size: .82rem; font-weight: 600; color: var(--accent-deep); cursor: pointer; padding: .2rem .4rem; border-radius: 8px; }
.mpd-foot button:hover { background: var(--accent-soft); }
/* font-family:inherit — es un <em>, y la regla global "em, .serif-em" (styles.css) le pone Fraunces
   por defecto; el área de miembros usa fuente del sistema en todo, esto no debía ser la excepción. */
.manage-row .req { font-family: inherit; color: var(--accent-deep); font-style: normal; font-size: .76rem; }
/* Fila para agregar un coach pre-aprobado: input en píldora, igual de redondeado que el botón. */
.coach-allow-row { gap: .6rem; }
/* El desplegable del cargo HEREDA `.manage-row select`, que ya trae la píldora, la flecha dibujada
   y su hueco de 2.3rem a la derecha. La versión anterior redefinía el padding y se comía ese hueco:
   la flecha quedaba pegada al borde. Acá solo se toca lo que hace falta.
   `min-width` baja de 200px porque en esta fila el correo necesita el espacio, y el rótulo más largo
   («Líder de consultores») cabe de sobra en 170px.
   ⚠️ El selector va con el elemento delante (`select.coach-allow-rol`, 0-1-1) y no como clase suelta
   (0-1-0): `.manage-row select` le gana en especificidad y su `min-width: 200px` se habría impuesto
   sin decir nada. Es la misma trampa que ya se pagó con `.manage-head span`. */
select.coach-allow-rol { flex: 0 0 auto; min-width: 170px; }
/* Aviso al pre-aprobar un cargo de mando: quien registre ese correo entra sin que nadie lo revise. */
.coach-allow-aviso { display: flex; align-items: flex-start; gap: .45rem; margin: .1rem 0 .6rem; padding: .5rem .65rem;
  background: var(--accent-soft); color: var(--accent-deep); border-radius: 10px; font-size: .82rem; line-height: 1.4; }
.coach-allow-aviso svg { flex: none; margin-top: .1rem; }
/* El cargo va de SUBTEXTO bajo el correo, con el mismo patrón que el plan bajo el nombre de un
   miembro (.member-id): negrita arriba, línea pequeña y apagada debajo. Antes era una pastilla al
   lado, que competía con el correo en vez de acompañarlo. */
.coach-allow-id { flex: 1 1 auto; min-width: 0; }
.coach-allow-id b { display: block; font-size: .88rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.coach-allow-id span { font-size: .78rem; color: var(--ink-mute); }
.coach-allow-input { flex: 1 1 auto; min-width: 0; padding: .5rem 1rem; border-radius: 100px; border: 1px solid var(--line); background: var(--bg); font: inherit; font-size: .9rem; line-height: 1.5; color: var(--ink); transition: border-color .25s, background .25s; }
.coach-allow-input:focus { outline: none; border-color: var(--accent); background: var(--paper); }

/* Término del programa ajustable (30-jul-2026) — dentro de "Gestionar miembro".
   Va DESPUÉS de .field (app.css:633) a propósito: .field trae margin-bottom:1.1rem, que dentro de
   una sección compacta abre un hueco enorme. Puesto antes, este override se ignoraría en silencio
   (misma trampa de cascada que ya mordió con .restricted-banner--action). */
.mend-meta { font-size: .76rem; color: var(--ink-mute); line-height: 1.5; margin: .1rem 0 .7rem; }
.mend-form { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; }
.mend-form .field { margin-bottom: .7rem; }
.mend-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
@media (min-width: 620px) {
  .mend-form { grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); column-gap: .7rem; }
  .mend-actions { grid-column: 1 / -1; }
}

/* ---------- "Marcar como no leído" (31-jul-2026, formato WhatsApp) ----------
   El "⋮" va FUERA del <button> de la fila (un botón dentro de otro es HTML inválido), así que la
   fila pasa a vivir en un envoltorio posicionado y el menú se superpone a la derecha.
   Aparece al pasar el ratón; en táctil, donde no hay hover, se queda siempre visible. */
/* El "⋮" va SIEMPRE visible (pedido del cliente, 31-jul-2026). Antes aparecía al pasar el ratón y
   PARPADEABA sin parar, por una razón concreta: al aparecer, la fila ensanchaba su margen derecho
   para hacerle sitio, el contenido se movía, el puntero dejaba de estar encima, el menú se ocultaba,
   la fila volvía a su ancho… y vuelta a empezar, varias veces por segundo. El culpable no era la
   transparencia sino el CAMBIO DE MAQUETA al pasar el ratón.
   Ahora el hueco está reservado siempre y el icono no se mueve, así que no hay nada que parpadear.
   Es además un atajo permanente a gestionar y eliminar el grupo, que es para lo que se quiere. */
.msg-thread-wrap { position: relative; }
/* SIN `transform`, y esto no es cosmético: el panel del menú se posiciona como `fixed` para
   escapar del recorte de la lista, y un ancestro con `transform` deja de tener a la VENTANA como
   referencia — el panel se colocaba respecto al propio icono y aterrizaba fuera de la pantalla.
   Efecto: se pulsaba, el panel se abría de verdad… y no se veía nada. Se centra con top/bottom+flex,
   que hace lo mismo sin crear esa referencia. */
.msg-thread-wrap .thread-menu { position: absolute; top: 0; bottom: 0; right: .5rem; display: flex; align-items: center; z-index: 2; }
.msg-thread-wrap .msg-thread { padding-right: 2.6rem; }

/* ---------- Grupos en Mensajes (31-jul-2026 · fase2/69) ----------
   Van DESPUÉS de .side-avatar y de la lista de mensajes: todo lo de acá son ajustes sobre esas
   reglas base y, puesto antes, se ignoraría en silencio. */
.side-avatar.is-group { background: var(--bg-3); color: var(--ink-soft); }
.side-avatar.is-group .app-icon { width: 18px; height: 18px; }
/* Cabecera "Grupos" con el botón de crear a la derecha, sin romper el resto de cabeceras. */
.msg-list-head--con-accion { display: flex; align-items: center; justify-content: space-between; gap: .6rem; }

/* Filtros del listado de Mensajes (11-ago-2026). Pegados bajo la cabecera y con el mismo fondo, para
   que se lean como parte de ella y no como una fila más de la lista. */
.msg-filtros { display: flex; gap: .4rem; padding: .1rem .75rem .55rem; background: var(--paper); }
.msg-filtro { display: inline-flex; align-items: center; gap: .35rem; border: 1px solid var(--line); background: var(--paper); color: var(--ink-mute);
  border-radius: 999px; padding: .22rem .7rem; font: inherit; font-size: .8rem; cursor: pointer; transition: border-color .18s, color .18s, background .18s; }
.msg-filtro:hover { border-color: var(--accent); color: var(--accent-deep); }
.msg-filtro.is-on { background: var(--ink); border-color: var(--ink); color: #fff; font-weight: 600; }
.msg-filtro-n { background: var(--accent); color: #fff; border-radius: 999px; min-width: 1.1rem; padding: 0 .28rem; font-size: .7rem; font-weight: 700; text-align: center; }
.msg-filtro.is-on .msg-filtro-n { background: var(--accent); }
.msg-group-new { background: none; border: none; cursor: pointer; color: var(--accent-deep); font: inherit; font-size: .74rem; font-weight: 600; display: inline-flex; align-items: center; gap: .3rem; padding: 0; text-transform: none; letter-spacing: 0; }
.msg-group-new:hover { text-decoration: underline; }
.msg-group-new .app-icon { width: 13px; height: 13px; }
/* Quién escribió, encima de la burbuja ajena. Con la lista de integrantes oculta, es lo único que
   dice quién está al otro lado — por eso lleva foto y no solo el nombre. */
.bubble-author { display: flex; align-items: center; gap: .45rem; margin: .5rem 0 .2rem; font-size: .76rem; color: var(--ink-mute); }
.bubble-author-av { width: 22px; height: 22px; font-size: .7rem; }
/* Elegir integrantes al crear/gestionar un grupo: NO tiene estilos propios. Reutiliza los de
   "Crear comunidad" (.msplit, .manage-row.is-team, .evt-cover-upload) para que las dos pestañas
   sean la misma maqueta de verdad y no dos parecidas que se separan con el tiempo. */
.msg-mini-av.is-group { display: grid; place-items: center; }
.msg-mini-av.is-group .app-icon { width: 15px; height: 15px; }

/* Mensajería */
.msg-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.2rem; align-items: stretch; }
/* Alto FIJO de los dos boxes (lista + chat abierto): no crecen con el contenido; si el listado no
   cabe, se desplaza con scroll vertical dentro del box (tipo WhatsApp). Ambos comparten el mismo alto. */
.msg-list { padding: .8rem; height: 70vh; overflow-y: auto; }
.msg-list-head { font-size: .72rem; color: var(--ink-mute); padding: .5rem .6rem .7rem; font-weight: 600; }
/* Separador entre la sección "Equipo" y "Conversaciones" en la lista */
.msg-thread + .msg-list-head { margin-top: .5rem; padding-top: .9rem; border-top: 1px solid var(--line-2); }
.msg-thread { display: flex; align-items: center; gap: .7rem; width: 100%; padding: .7rem; border-radius: 12px; transition: background .2s; text-align: left; }
/* ---------- Arrastrar hacia la derecha para marcar como no leída (9-ago-2026) ----------
   ⚠️ El `transform` va en `.msg-thread`, NUNCA en `.msg-thread-wrap`. Un ancestro transformado deja
   al panel del menú «⋮» sin la ventana como referencia y lo manda fuera de la pantalla — está
   avisado unas líneas más arriba, y es exactamente la trampa en la que se caería aquí.
   La fila necesita fondo propio y quedar por ENCIMA: si no, se vería el panel verde a través. */
.msg-thread-wrap .msg-thread { position: relative; z-index: 1; background: var(--paper); }
.msg-thread-wrap:not(.arrastrando) .msg-thread { transition: background .2s, transform .18s var(--ease-out); }
/* Solo el icono: sin rótulo, el hueco que va apareciendo al arrastrar se lee de un vistazo y no
   depende del ancho de la fila (con «Marcar como no leída» el texto se cortaba en las estrechas). */
.thread-swipe {
  position: absolute; inset: 0; z-index: 0; display: flex; align-items: center;
  padding-left: 1.1rem; border-radius: 12px; opacity: 0; pointer-events: none;
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  color: var(--accent-deep);
}
.msg-thread-wrap.arrastrando .thread-swipe { opacity: 1; }
/* Más grande que los 16px de siempre: ahora va solo y tiene que sostener el gesto él mismo. */
.thread-swipe .app-icon { width: 21px; height: 21px; }
@media (prefers-reduced-motion: reduce) { .msg-thread-wrap .msg-thread { transition: background .2s; } }
.msg-thread:hover { background: var(--bg); }
.msg-thread.is-active { background: var(--grad-box); }
.msg-thread.is-active b, .msg-thread.is-active .mt-info span { color: var(--bg); }
.mt-info { min-width: 0; }
.mt-info b { display: block; font-size: .88rem; }
.mt-info span { font-size: .76rem; color: var(--ink-mute); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; max-width: 180px; }
.msg-conv { display: flex; flex-direction: column; height: 70vh; }
.conv-head { display: flex; align-items: center; gap: .8rem; padding: 1.1rem 1.3rem; border-bottom: 1px solid var(--line-2); }
/* Solo el span HIJO DIRECTO (el subtítulo) es bloque; con `> span` NO afecta al avatar ni al
   <span class="ceo-badge"> anidado en el <b> del nombre (antes lo volvía una barra a todo el ancho). */
.conv-head-id b { font-size: .95rem; } .conv-head-id > span { font-size: .76rem; color: var(--ink-mute); display: block; }
.conv-body { flex: 1; overflow-y: auto; padding: 1.3rem; display: flex; flex-direction: column; gap: .7rem; }
.bubble { max-width: 75%; padding: .7rem 1rem; border-radius: 16px; background: var(--bg); align-self: flex-start; overflow-wrap: break-word; }
.bubble.mine { background: var(--accent); color: #fff; align-self: flex-end; }
.bubble p { font-size: .9rem; }
.bubble time { font-size: .66rem; opacity: .6; display: block; margin-top: .25rem; }
.conv-form { display: flex; gap: .6rem; padding: 1rem 1.3rem; border-top: 1px solid var(--line-2); }
/* input Y textarea: al pasar el campo a <textarea> estos estilos dejaron de aplicar y el campo
   perdió su forma de píldora. Van juntos para que no vuelva a pasar. */
.conv-form input,
.conv-form textarea { flex: 1; padding: .7rem 1rem; border-radius: 20px; border: 1px solid var(--line); background: var(--bg); font: inherit; font-size: .9rem; outline: none; }
.conv-form input:focus,
.conv-form textarea:focus { border-color: var(--accent); }
.msg-empty { color: var(--ink-mute); font-size: .88rem; text-align: center; padding: 1.4rem; }
/* Indicador "escribiendo…" en vivo (estilo WhatsApp): burbuja a la izquierda con 3 puntos que rebotan. */
.conv-typing { align-self: flex-start; }
.conv-typing-dots { display: inline-flex; align-items: center; gap: 4px; background: var(--bg-2); border-radius: 16px; padding: .6rem .85rem; }
.conv-typing-dots i { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-mute); animation: convDot 1.2s infinite ease-in-out; }
.conv-typing-dots i:nth-child(2) { animation-delay: .18s; }
.conv-typing-dots i:nth-child(3) { animation-delay: .36s; }
@keyframes convDot { 0%, 60%, 100% { opacity: .35; transform: translateY(0); } 30% { opacity: .9; transform: translateY(-3px); } }
/* Estado "Inicia la conversación…" centrado verticalmente en el cuerpo del chat */
.conv-body > .msg-empty { margin: auto 0; }
.msg-placeholder { display: grid; place-items: center; height: 100%; color: var(--ink-mute); gap: .6rem; }
.msg-placeholder .app-icon { width: 30px; height: 30px; }

@media (min-width: 700px) { .members-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 980px) {
  .members-grid { grid-template-columns: repeat(3, 1fr); }
}
/* .msg-layout tiene una columna fija (300px) como .feed-layout: mismo ajuste de breakpoint que
   arriba (R11.2) — recién a los ~980px de contenido REAL (viewport - 264px de sidebar). */
@media (min-width: 1240px) {
  /* 300 → 340px (5-ago-2026): el listado quedaba compacto; el chat sigue con minmax(0,1fr). */
  .msg-layout { grid-template-columns: 340px minmax(0, 1fr); }
}
/* Sin conversación elegida (1-ago-2026): en DOS columnas el cartel "Elige una conversación" ocupa un
   hueco que existe igual, así que se deja. Por debajo de 1240px los dos boxes van apilados, y ahí el
   mismo cartel serían 70vh de vacío colgando bajo el listado: se recoge. Va DESPUÉS de `.msg-conv`
   —que trae el display: flex— porque si no, la regla de arriba gana y no se ve ningún efecto. */
@media (max-width: 1239.98px) {
  .msg-layout--sin-chat .msg-conv { display: none; }
}


/* ===== Alta de "Miembro activo" · la pestaña completa (2-ago-2026) =====
   Dos columnas: a la izquierda los datos que definen al alumno (plan, consultor, fecha, sesiones),
   a la derecha sus módulos, que es lo largo y lo único que necesita desplazarse. El pie con el
   resumen y los botones queda SIEMPRE a la vista: es lo que dice qué va a pasar al guardar. */
.app-modal-card.alta-modal-card { max-width: 1040px; width: min(1040px, 96vw);
  display: flex; flex-direction: column; max-height: min(88vh, 860px); }
.alta-modal { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; gap: 1rem; }
.alta-head { flex: 0 0 auto; }
.alta-head h4 { margin: 0 0 .35rem; }
.alta-head .m-note { margin: 0; }
/* minmax(0, …) en LAS DOS columnas: con `1fr` a secas, un título largo de módulo ensancha la
   columna y descuadra la grilla entera. */
.alta-split { display: grid; grid-template-columns: minmax(0, 320px) minmax(0, 1fr); gap: 1.4rem;
  flex: 1 1 auto; min-height: 0; }
.alta-col { min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.alta-col--datos { overflow-y: auto; overscroll-behavior: contain; padding-right: .3rem; }
.alta-col--mods { border-left: 1px solid var(--line-2); padding-left: 1.4rem; }
.alta-sesiones { margin-top: .4rem; }
/* `.hint` solo está definido dentro de `.field`; acá cuelga de la caja de sesiones, así que hay que
   darle su tamaño y su gris o sale como texto normal, pesando más que el bloque al que explica. */
.alta-sesiones .hint { font-size: .72rem; color: var(--ink-mute); margin-top: .5rem; }
/* Campo obligatorio sin rellenar al intentar guardar el alta. Se marca el borde y la ayuda, y se
   quita en cuanto se elige algo — no se queda encendido reprochando. */
.field.is-error select { border-color: var(--accent-deep); }
.field.is-error .hint { color: var(--accent-deep); }
.alta-mods-head { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: .5rem; margin-bottom: .7rem; }
.alta-todos { font-size: .72rem; color: var(--ink-mute); display: flex; align-items: center; gap: .3rem; flex-wrap: wrap; }
/* `display:flex` gana sobre el `hidden` del navegador: sin esto, esconderlo no lo esconde. */
.alta-todos[hidden] { display: none; }
.alta-mini { border: 1px solid var(--line); background: transparent; color: var(--ink-mute);
  border-radius: 999px; padding: .18rem .55rem; font: inherit; font-size: .72rem; cursor: pointer;
  transition: border-color .2s, color .2s, background-color .2s; }
.alta-mini:hover { border-color: var(--accent); color: var(--accent); }
.alta-mods { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding-right: .3rem; }
.alta-vacio { display: flex; align-items: center; gap: .5rem; padding: 1.6rem .2rem; }
.alta-vacio .app-icon { width: 18px; height: 18px; flex: 0 0 auto; }

.alta-mod { border: 1px solid var(--line-2); border-radius: 12px; margin-bottom: .55rem; overflow: hidden;
  transition: border-color .2s; }
.alta-mod.is-hecho { border-color: rgba(242,107,94,.45); }
.alta-mod-head { display: flex; align-items: center; gap: .6rem; padding: .55rem .7rem; flex-wrap: wrap; }
.alta-mod-title { flex: 1 1 220px; min-width: 0; display: flex; align-items: center; gap: .5rem;
  background: none; border: none; padding: 0; font: inherit; color: inherit; text-align: left; cursor: pointer; }
.alta-mod-title > span { min-width: 0; }
.alta-mod-title b { display: block; font-size: .86rem; font-weight: 500; }
.alta-mod-title small { display: block; font-size: .72rem; color: var(--ink-mute); margin-top: .1rem; }
.alta-chev { width: 15px; height: 15px; flex: 0 0 auto; color: var(--ink-mute);
  transition: transform .3s var(--ease-out); }
.alta-mod.is-open .alta-chev { transform: rotate(180deg); }
/* Tres cápsulas SEPARADAS, no un control segmentado: son tres decisiones distintas y así se lee
   cuál está activa sin tener que mirar el fondo del grupo (pedido del cliente). */
.alta-pills { display: flex; gap: .3rem; flex: 0 0 auto; }
.alta-pill { border: 1px solid var(--line); background: transparent; color: var(--ink-mute);
  border-radius: 999px; padding: .28rem .7rem; font: inherit; font-size: .74rem; cursor: pointer;
  white-space: nowrap; transition: border-color .2s, color .2s, background-color .2s; }
.alta-pill:hover { border-color: var(--ink-mute); color: var(--ink); }
.alta-pill.is-on { background: var(--accent); border-color: var(--accent); color: #fff; }
.alta-tareas { border-top: 1px solid var(--line-2); background: var(--bg); }
.alta-tareas .task-head { padding: .5rem .8rem; }
.alta-tareas .task-head .t b { font-size: .82rem; }
.alta-tareas .task-head .t span { font-size: .7rem; }
.alta-tareas .lesson-check { width: 20px; height: 20px; }
.alta-tareas .lesson-check .app-icon { width: 12px; height: 12px; }

.alta-pie { flex: 0 0 auto; border-top: 1px solid var(--line-2); padding-top: .9rem;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.alta-resumen { margin: 0; font-size: .76rem; color: var(--ink-mute); min-width: 0; }
.alta-resumen b { color: var(--ink); font-weight: 600; }
.alta-btns { display: flex; gap: .5rem; flex-wrap: wrap; }

@media (max-width: 900px) {
  .alta-split { grid-template-columns: minmax(0, 1fr); }
  .alta-col--datos { overflow: visible; }
  .alta-col--mods { border-left: none; border-top: 1px solid var(--line-2); padding-left: 0; padding-top: 1rem; }
  .alta-mods { overflow: visible; }
  .app-modal-card.alta-modal-card { max-height: 92vh; }
  .alta-modal { overflow-y: auto; }
}

/* =============================================================
   13. ONBOARDING · NOTIFICACIONES · BANNER
   ============================================================= */
/* Sidebar link destacado (onboarding pendiente) */
.side-link--accent { background: var(--accent-soft); color: var(--accent-deep); }
.side-link--accent .app-icon { stroke: var(--accent-deep); }
.side-link--accent:hover { background: var(--accent-soft); color: var(--accent-deep); }
.side-link--accent:hover .app-icon { stroke: var(--accent-deep); }
.dot-pend { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); margin-left: auto; box-shadow: 0 0 0 0 var(--accent-glow); animation: livePulse 2.2s infinite; }

/* Banner onboarding: reusa .restricted-banner (mismo fondo oscuro sólido que Panel de métricas y
   Seguimiento de alumnos, en vez del degradado negro-coral que tenía antes — pedido del cliente,
   13-jul-2026, para unificar el estilo de todos los avisos de "acceso restringido" del sitio). */
.restricted-banner .onb-banner-cta { margin-left: auto; font-weight: 600; white-space: nowrap; }

/* Notificaciones */
.notif { position: relative; }
/* Tope inferior: el panel debe terminar a la MISMA altura que la pestaña de mensajes de la burbuja
   (.msg-mini, anclada con `bottom: 5.35rem`). Sin tope crecía con el número de notificaciones hasta
   salirse de la pantalla y las últimas quedaban fuera de alcance. El valor exacto lo calcula
   sizeNotifPanel() en app.js al abrirlo (el borde superior depende del alto real del topbar, que
   cambia con el ancho porque el título usa font-size con clamp); este max-height es solo el respaldo
   por si el JS no alcanza a correr. `overflow-y: auto` es lo que hace scrolleable el excedente. */
/* Ancho 375px = los 300px originales +25% (pedido 28-jul-2026). Crece SOLO hacia la izquierda porque
   está anclado con `right: 0` a la campana: el borde derecho no se mueve, el izquierdo se corre 75px. */
.notif-panel { position: absolute; top: calc(100% + 10px); right: 0; width: 375px; max-width: 84vw; z-index: 80;
  background: var(--paper); border: 1px solid var(--line-2); border-radius: 16px; box-shadow: 0 24px 60px -24px rgba(28,26,24,.5);
  padding: .6rem; animation: viewIn .25s var(--ease-out);
  max-height: calc(100vh - 10rem); overflow-y: auto; overscroll-behavior: contain; }
.notif-head { font-size: .72rem; color: var(--ink-mute); font-weight: 600; padding: .6rem .7rem .4rem;
  display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
/* Interruptor del sonido de los avisos. Vive en la cabecera del panel de la campana porque es donde
   la persona está pensando en sus notificaciones; la preferencia se guarda por dispositivo. */
.notif-snd { display: inline-flex; align-items: center; gap: .3rem; border: 1px solid var(--line-2);
  background: var(--bg); color: var(--ink-mute); border-radius: 100px; padding: .18rem .55rem;
  font-size: .68rem !important; font-weight: 600; cursor: pointer; }
.notif-snd:hover { border-color: var(--ink-mute); color: var(--ink); }
.notif-snd .app-icon { width: 13px; height: 13px; }
.notif-item { display: flex; gap: .7rem; align-items: flex-start; padding: .8rem .7rem; border-radius: 10px; width: 100%; text-align: left; transition: background .2s; }
.notif-item:hover { background: var(--bg); }
/* Leída (contrato 24-jul-2026): se queda 48h en la barra, atenuada; ya no cuenta en el dot. */
.notif-item.is-read { opacity: .55; }
/* Deep-links de notificaciones (spec 26-jul-2026): aterrizaje con resaltado breve. */
/* Subtítulos del Panel de solicitudes («Upsell», «Pausas»): MISMA tipografía que el título del panel
   (.track-title) — Raleway (var(--serif) dentro de .app-body), 700 y el mismo espaciado. Antes iban
   con la fuente del sistema, en gris y con las letras muy separadas, y se leían como de otra familia.
   No se fija color: así heredan el mismo que el título, igual que hace él. */
.sol-sub { display: flex; align-items: center; gap: .45rem; margin: 1rem 0 .5rem;
  font-family: var(--serif); font-size: 1.15rem; font-weight: 700; letter-spacing: -.01em; }
.sol-sub .app-icon { width: 15px; height: 15px; }
.sol-flash { box-shadow: 0 0 0 2px #F26B5E, 0 8px 24px rgba(242, 107, 94, .18); transition: box-shadow .5s ease; }
.task-flash { outline: 2px solid #F26B5E; outline-offset: 3px; border-radius: 12px; transition: outline-color .5s ease; }
.notif-ico { width: 34px; height: 34px; border-radius: 10px; background: var(--accent-soft); color: var(--accent-deep); display: grid; place-items: center; flex: 0 0 auto; }
.notif-item b { font-size: .88rem; display: block; }
.notif-item span { font-size: .78rem; color: var(--ink-mute); }
.notif-empty { padding: 1.4rem .7rem; text-align: center; color: var(--ink-mute); font-size: .85rem; }

/* Menú de la cuenta (avatar del topbar) */
.topbar-user { position: relative; flex: 0 0 auto; }
button.topbar-avatar { border: none; padding: 0; cursor: pointer; font: inherit; }
.user-menu { position: absolute; top: calc(100% + 10px); right: 0; min-width: 232px; z-index: 80;
  background: var(--paper); border: 1px solid var(--line-2); border-radius: 14px; box-shadow: 0 24px 60px -24px rgba(28,26,24,.5);
  padding: .4rem; animation: viewIn .25s var(--ease-out); }
.user-menu-item { display: flex; align-items: center; gap: .6rem; width: 100%; text-align: left; padding: .65rem .7rem; border-radius: 9px; font-size: .9rem; color: var(--ink); transition: background .15s; cursor: pointer; white-space: nowrap; }
.user-menu-item:hover { background: var(--bg); }
.user-menu-item .app-icon { width: 16px; height: 16px; color: var(--ink-soft); flex: 0 0 auto; }
.user-menu-item + .user-menu-item { border-top: 1px solid var(--line-2); }

/* Onboarding view */
.onb-intro { max-width: 640px; margin-bottom: 1.8rem; }
.onb-intro h2 { font-family: var(--serif); font-weight: 300; font-size: clamp(1.8rem,4vw,2.4rem); }
.onb-intro p { color: var(--ink-soft); margin-top: .6rem; }
.coaches-grid { display: grid; grid-template-columns: 1fr; gap: 1.2rem; }
.coach-card { padding: 1.6rem 1.5rem; text-align: center; display: flex; flex-direction: column; align-items: center; }
.coach-avatar { width: 84px; height: 84px; border-radius: 50%; background: var(--accent-soft); color: var(--accent-deep);
  display: grid; place-items: center; font-family: var(--serif); font-style: italic; font-size: 2rem; font-weight: 600; }
.coach-card h3 { font-family: var(--serif); font-weight: 400; font-size: 1.3rem; margin-top: 1rem; }
.coach-role { font-size: .78rem; color: var(--accent-deep); font-weight: 600; margin-top: .3rem; }
.coach-bio { color: var(--ink-soft); font-size: .9rem; margin: .8rem 0 1.4rem; }
.coach-card .btn { margin-top: auto; width: 100%; }
.onb-done { padding: clamp(2rem,5vw,3rem); text-align: center; max-width: 560px; margin: 0 auto; }
.onb-done .gate-ico { background: #e2f4ea; color: #1f7a4d; }
.onb-done h2 { font-family: var(--serif); font-weight: 300; font-size: 1.8rem; }
.onb-done p { color: var(--ink-soft); margin: .8rem auto 1.4rem; max-width: 42ch; }

/* Welcome popup (reusa modal). Diseño aprobado por el cliente vía Artifact (11-jul-2026):
   foto circular de Nubia + kicker + título editorial, celebrando la decisión de empezar en vez
   de un saludo genérico. Ancho propio (.welcome-modal, ver .app-modal-card más abajo) para que
   la primera oración del texto quepa en una sola línea. Usa Fraunces real (no var(--serif), que
   en el área de miembros es Raleway) para lograr el mismo look editorial de "Sobre Nubia" en la
   portada — única excepción puntual en esta tarjeta; !important necesario para ganarle a las
   reglas globales de tipografía del área de miembros (font-size:14px y font-weight:700 en
   .welcome-card h3, ambas más abajo en este archivo). */
.welcome-card { text-align: center; }
.welcome-kicker { display: block; font-size: .66rem !important; font-weight: 600 !important; letter-spacing: .03em; color: var(--accent-deep); margin-bottom: .85rem; }
.welcome-ico { width: 92px; height: 92px; margin: 0 auto 1rem; border-radius: 50%; overflow: hidden; border: 3px solid var(--accent-soft); }
.welcome-ico img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Tipografía calcada de .auth-brand h1 (styles.css) — el título del panel oscuro de login/registro
   ("Tu marca empieza hoy.") — para que el título de bienvenida se vea con la misma personalidad
   editorial (13-jul-2026, antes calcaba .hero h1; el cliente pidió igualarlo a este otro título en
   su lugar). El font-size se mantiene un valor fijo en rem (no la fórmula clamp(...vw...) del
   original, pensada para su propio panel/ancho) equivalente al tope de su clamp (3rem). */
.welcome-card h3 { font-family: "Fraunces", Georgia, "Times New Roman", serif !important; font-weight: 300 !important; font-size: 3rem !important; line-height: 1.05; letter-spacing: -0.02em; }
/* font-size:inherit !important — sin esto, la regla global del área de miembros
   (font-size:14px !important en todo .app-body *) le gana al <em>, dejando "primer paso." chico
   en vez de heredar el tamaño grande del <h3>. font-family:"Fraunces" explícito — sin esto, la
   regla global del sitio "em{font-family:var(--serif)}" (styles.css) gana por sobre la herencia del
   <h3>, y en el área de miembros --serif es Raleway (no Fraunces), dejando "primer paso." en la
   tipografía equivocada. font-weight:400 (no 300) — igual que el <em> "hoy." de referencia, que por
   esa misma regla global (em{font-weight:400}) renderiza un poco más grueso que el resto del título. */
.welcome-card h3 em { font-family: "Fraunces", Georgia, "Times New Roman", serif !important; font-style: italic; font-weight: 400 !important; font-size: inherit !important; color: var(--accent); }
/* Sin max-width en ch: con el <br> explícito entre las 2 oraciones, el ancho del modal (500px,
   ver .welcome-modal) ya alcanza para que cada una quede en una sola línea; un max-width en ch
   partía la segunda oración (más larga) en dos líneas. */
.welcome-card p { color: var(--ink-soft); margin: .8rem auto 1.4rem; line-height: 1.5; }

@media (min-width: 720px) { .coaches-grid { grid-template-columns: repeat(3, 1fr); } }

/* =============================================================
   14. CALENDARIO (eventos) · MENSAJERÍA (adjuntos) · MODAL
   ============================================================= */
/* Crear evento (solo admin) */
.cal-create { width: 100%; justify-content: center; gap: .55rem; margin-bottom: 1rem; padding: 1rem; border: 0; color: #fff; background: var(--grad-portada); border-radius: var(--radius); box-shadow: 0 14px 34px -20px rgba(28,26,24,.55); }
.cal-create:hover { background: var(--grad-portada); filter: brightness(1.07); box-shadow: 0 18px 40px -18px rgba(28,26,24,.6); }
.cal-create .app-icon { width: 16px; height: 16px; }
.cal-evt:hover { filter: brightness(.96); }
.cal-more { align-self: flex-start; font-size: .6rem; color: var(--ink-mute); font-weight: 600; }

/* ===== Vista de día del calendario (agenda con horario, estilo iOS) =====
   Se abre al pulsar el CUADRO del día. La tarjeta no scrollea: scrollea la franja horaria interna
   (por eso `overflow: hidden` acá y `flex: 1 1 auto; min-height: 0` en .dayv-scroll — sin el
   min-height, un hijo flexible nunca se encoge por debajo de su contenido y el scroll no aparece). */
/* La agenda del día también gana ancho (520 → 620): es donde se reparten en columnas los eventos que
   se pisan, así que cada bloque se lee mejor. El alto se mantiene: es una franja horaria con scroll. */
.app-modal-card.dayv-modal { max-width: 620px; width: min(620px, 94vw); height: min(86vh, 720px); display: flex; flex-direction: column; overflow: hidden; }
.dayv-head { flex: 0 0 auto; }
.dayv-dow { font-size: .74rem; color: var(--ink-mute); }
.dayv-modal .modal-title { margin: .1rem 0 .25rem; }
.dayv-count { display: block; font-size: .78rem; font-weight: 600; color: var(--evt-ink); margin-bottom: .9rem; }
.dayv-nohour { flex: 0 0 auto; display: flex; flex-direction: column; gap: .4rem; border: 1px solid var(--line-2); border-radius: 12px; padding: .6rem .7rem; margin-bottom: .9rem; }
.dayv-nohour .lbl { font-size: .66rem; color: var(--ink-mute); font-weight: 600; }
.dayv-chip { text-align: left; border: none; background: var(--evt-soft); color: var(--evt-ink); border-radius: 9px; padding: .42rem .6rem; cursor: pointer; }
.dayv-chip:hover { filter: brightness(.97); }
.dayv-chip b { display: block; font-size: .84rem; }
.dayv-chip span { font-size: .72rem; opacity: .85; }
.dayv-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; border-top: 1px solid var(--line-2); padding: 14px 0 26px; }
.dayv-grid { position: relative; }
.dayv-line { position: absolute; left: 52px; right: 2px; height: 0; border-top: 1px solid var(--line-2); }
.dayv-line i { position: absolute; left: -52px; top: -7px; width: 44px; text-align: right; font-style: normal; font-size: .64rem; color: var(--ink-mute); font-variant-numeric: tabular-nums; }
.dayv-lane { position: absolute; left: 56px; right: 2px; top: 0; bottom: 0; }
.dayv-ev { position: absolute; overflow: hidden; text-align: left; padding: .3rem .5rem; border: none; border-left: 3px solid var(--evt); border-radius: 8px; background: var(--evt-soft); color: var(--evt-ink); cursor: pointer; }
.dayv-ev:hover { filter: brightness(.97); }
/* Una línea cada uno: en un bloque angosto (eventos que se pisan) el texto se partía en tres renglones
   y quedaba cortado a media palabra. Con nowrap + ellipsis se recorta limpio. */
.dayv-ev b, .dayv-ev span { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dayv-ev b { font-size: .8rem; line-height: 1.3; }
.dayv-ev span { font-size: .68rem; opacity: .9; font-variant-numeric: tabular-nums; }
/* Línea de "ahora": solo se pinta si el día abierto es hoy. */
.dayv-now { position: absolute; left: 50px; right: 2px; height: 0; border-top: 2px solid var(--accent); z-index: 2; }
.dayv-now::before { content: ""; position: absolute; left: -4px; top: -5px; width: 9px; height: 9px; border-radius: 50%; background: var(--accent); }

/* Evento en la lista: host + clic para detalle */
.evt-item[data-evt-open] { cursor: pointer; }
.evt-host { display: flex; align-items: center; gap: .5rem; margin-top: .55rem; }
.evt-host-av { width: 26px; height: 26px; font-size: .72rem; flex: 0 0 auto; }
.evt-host-meta { font-size: .8rem; color: var(--ink-soft); line-height: 1.15; }
.evt-host-meta small { display: block; font-size: .7rem; color: var(--ink-mute); }
.muted-pill { align-self: center; font-size: .76rem; color: var(--ink-mute); border: 1px dashed var(--line); border-radius: 100px; padding: .4rem .8rem; white-space: nowrap; }

/* Modal: título, cerrar (×) y formulario de evento */
.modal-title { font-family: var(--serif); font-weight: 300; font-size: 1.6rem; margin: .2rem 0 1.2rem; padding-right: 2rem; }
.app-modal-x { position: absolute; top: 1rem; right: 1rem; width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--line); background: var(--bg); display: grid; place-items: center; color: var(--ink-soft); }
.app-modal-x:hover { border-color: var(--ink); color: var(--ink); }
.app-modal-x .app-icon { width: 16px; height: 16px; transform: rotate(45deg); }
.evt-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 1rem; }
/* Horario del evento: dos desplegables (inicio a término) en una fila */
.evt-time-row { display: flex; align-items: center; gap: .5rem; }
.evt-time-row select { flex: 1 1 0; min-width: 0; }
.evt-time-sep { flex: 0 0 auto; color: var(--ink-mute); font-size: .82rem; }

/* Dropzone (arrastrar y soltar un archivo/imagen): resalte al arrastrar encima, con `outline` para
   no empujar el layout. Se aplica a todas las subidas por formulario (portadas, entregables,
   publicación, avatar). El .composer-drop-hint solo se muestra en escritorio, donde hay arrastre. */
/* OJO: NO transicionar outline-color. La barra del chat se re-dibuja en cada envío/grabación/adjunto y,
   al añadirse .is-dropzone, el color inicial del outline es `currentcolor` (oscuro): con transición se
   veía animar de oscuro→transparente = un destello punteado intermitente. Sin transición aparece ya en
   transparente (invisible) y el resalte de arrastre entra igual (instantáneo). */
.is-dropzone { transition: background .15s; outline: 2px dashed transparent; outline-offset: 3px; }
.is-dropzone.is-dragover { outline-color: var(--accent); background: var(--accent-soft); }
@media (hover: none) { .composer-drop-hint { display: none; } }

/* Subida de foto de portada (formulario de evento) */
.evt-cover-upload { display: flex; align-items: center; gap: 1rem; }
.evt-cover-preview { width: 132px; height: 80px; flex: 0 0 auto; border-radius: 12px; border: 1px dashed var(--line); background: var(--bg) center/cover no-repeat; display: grid; place-items: center; overflow: hidden; }
.evt-cover-preview.has-img { border-style: solid; border-color: transparent; }
.evt-cover-ph { font-size: .72rem; color: var(--ink-mute); display: inline-flex; align-items: center; gap: .35rem; }
.evt-cover-ph .app-icon { width: 15px; height: 15px; }
.evt-cover-up-actions { display: flex; flex-direction: column; align-items: flex-start; gap: .4rem; }
.evt-cover-up-actions .field-hint { margin-top: 0; }
/* Enlace de reunión + botón de generación automática (Google Meet, checkpoint 12.B) */
.evt-link-row { display: flex; gap: .6rem; align-items: center; }
.evt-link-row input { flex: 1; min-width: 0; }
.evt-link-row button { flex: 0 0 auto; white-space: nowrap; }

/* Detalle de evento */
/* Banner de portada (full-bleed sobre el modal) */
/* Portada del evento. SIN imagen: la caja degradada de 200px con el título encima, como siempre. */
.evt-cover { margin: -1.6rem -1.6rem 1.2rem; height: 200px; background: var(--grad-portada) center/cover no-repeat; border-radius: var(--radius-lg) var(--radius-lg) 0 0; display: flex; align-items: flex-end; padding: 1.3rem 1.5rem; }
/* CON imagen: la caja se adapta a la PROPORCIÓN de la portada en vez de imponerle 200px de alto.
   Antes era un fondo con `cover` sobre una caja fija, así que un afiche vertical —los de las
   masterclass— se recortaba por la mitad y se perdían el título y la fecha (3-ago-2026).
   `height:auto` deja mandar a la imagen; el tope evita que una portada muy alta empuje el resto de
   la ficha fuera de la pantalla, y `object-fit:cover` hace que ahí recorte en vez de deformarse.
   El tope combina las dos medidas que importan: 60vh para que en una ventana baja siga cabiendo el
   contenido, y 480px para que en una pantalla grande no se coma la ficha entera. Con la ficha a sus
   560px de ancho, eso deja pasar entera cualquier portada apaisada y casi entera una cuadrada; solo
   un afiche muy vertical se recorta, y ahí recortar es preferible a enterrar el botón de unirse. */
.evt-cover.has-img { display: block; height: auto; padding: 0; background: var(--bg-3); overflow: hidden; }
.evt-cover.has-img img { display: block; width: 100%; height: auto; max-height: min(60vh, 480px);
                         object-fit: cover; object-position: center; }
.evt-cover-title { font-family: var(--serif); font-weight: 300; font-size: 1.7rem; line-height: 1.05; color: var(--paper); letter-spacing: -.01em; }
.app-modal-x--over { background: rgba(255,255,255,.92); border-color: transparent; color: var(--ink); z-index: 2; box-shadow: 0 4px 14px -6px rgba(28,26,24,.5); }
.app-modal-x--over:hover { background: #fff; color: var(--ink); }
.evt-detail-tag { display: inline-block; font-size: .66rem; color: var(--evt-ink); font-weight: 600; }
.evt-detail-meta { display: flex; flex-wrap: wrap; gap: 1rem; font-size: .85rem; color: var(--ink-mute); margin-bottom: 1.1rem; }
.evt-detail-meta .app-icon { width: 15px; height: 15px; vertical-align: -2px; }
.evt-detail-host { display: flex; align-items: center; gap: .8rem; padding: .9rem 1rem; background: var(--bg); border-radius: 14px; margin-bottom: 1.1rem; }
.evt-detail-av { width: 50px; height: 50px; font-size: 1.1rem; flex: 0 0 auto; }
.evt-detail-host .lbl { display: block; font-size: .66rem; color: var(--ink-mute); }
.evt-detail-host b { font-size: 1rem; }
.evt-detail-host small { display: block; font-size: .78rem; color: var(--ink-mute); }
.evt-detail-intro { margin-bottom: 1.3rem; }
.evt-detail-intro .lbl { display: block; font-size: .66rem; color: var(--ink-mute); margin-bottom: .3rem; }
.evt-detail-intro p { font-size: .92rem; color: var(--ink-soft); }
.evt-detail-actions { display: flex; flex-wrap: wrap; gap: .7rem; }
.btn-danger { color: #b4453a; border-color: #e7c4bf; }
.btn-danger .app-icon { width: 15px; height: 15px; }

/* Badge CEO */
.ceo-badge { display: inline-block; font-size: .58rem; font-weight: 700; letter-spacing: .08em; vertical-align: 1px; background: var(--ink); color: var(--bg); border-radius: 100px; padding: .1rem .42rem; }
.msg-thread.is-active .ceo-badge { background: var(--accent); color: #fff; }

/* Herramientas del chat: imagen + voz */
.conv-tool { flex: 0 0 auto; width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--line); background: var(--bg); display: grid; place-items: center; color: var(--ink-soft); transition: border-color .2s, color .2s, background .2s; }
.conv-tool:hover { border-color: var(--accent); color: var(--accent-deep); }
.conv-tool .app-icon { width: 18px; height: 18px; }
.conv-tool.is-rec { background: var(--accent); border-color: var(--accent); color: #fff; animation: livePulse 1.4s infinite; }
/* Barra de grabación (timer estilo WhatsApp): reemplaza el input mientras se graba; solo queda el micrófono. */
.conv-rec-bar { display: none; flex: 1 1 auto; align-items: center; gap: .55rem; padding: 0 .3rem; min-width: 0; }
.conv-form.is-recording .conv-rec-bar { display: flex; }
/* Mientras se graba solo quedan el micrófono (dentro de la barra queda la papelera). Se apartan por
   ESTRUCTURA, no por el nombre del campo de cada superficie: antes la regla nombraba
   [data-conv-input]/[data-asst-input] y no conocía [data-msg-mini-input], así que en la burbuja de
   Mensajes el campo se quedaba peleando el ancho con la barra y el cronómetro no se veía. El
   micrófono tampoco entraba en la excepción ([data-mini-rec]) y desaparecía: no había forma de
   detener la grabación. */
.conv-form.is-recording .conv-more-wrap,
.conv-form.is-recording textarea,
.conv-form.is-recording > input[type="text"],
.conv-form.is-recording > button[type="submit"] { display: none; }
.conv-rec-dot { width: 11px; height: 11px; border-radius: 50%; background: #E5484D; flex: 0 0 auto; animation: recPulse 1.2s ease-in-out infinite; }
.conv-rec-time { font-variant-numeric: tabular-nums; font-weight: 600; font-size: .92rem; color: var(--ink); flex: 0 0 auto; min-width: 40px; }
.conv-rec-hint { font-size: .8rem; color: var(--ink-mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@keyframes recPulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.82); } }
/* Descartar la grabación sin enviarla. Antes el único botón era el micrófono, que SIEMPRE enviaba:
   una vez empezada la grabación no había salida. */
.conv-rec-cancel { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%; border: none; background: transparent; color: var(--ink-mute); display: grid; place-items: center; cursor: pointer; transition: background .15s, color .15s; }
.conv-rec-cancel:hover { background: rgba(229,72,77,.12); color: #E5484D; }
.conv-rec-cancel .app-icon { width: 16px; height: 16px; }
/* Onda animada mientras se graba (decorativa: no representa el volumen real del micrófono). */
.conv-rec-wave { display: inline-flex; align-items: center; gap: 3px; height: 18px; flex: 0 0 auto; }
.conv-rec-wave i { display: block; width: 3px; height: 100%; border-radius: 2px; background: var(--accent); transform: scaleY(.3); animation: recWave 1.05s ease-in-out infinite; }
.conv-rec-wave i:nth-child(2) { animation-delay: .1s; }
.conv-rec-wave i:nth-child(3) { animation-delay: .2s; }
.conv-rec-wave i:nth-child(4) { animation-delay: .3s; }
.conv-rec-wave i:nth-child(5) { animation-delay: .18s; }
.conv-rec-wave i:nth-child(6) { animation-delay: .08s; }
.conv-rec-wave i:nth-child(7) { animation-delay: .26s; }
@keyframes recWave { 0%, 100% { transform: scaleY(.28); } 50% { transform: scaleY(1); } }
/* Barra oscura de la burbuja de Mensajes: el texto por defecto es tinta sobre crema y ahí no se lee. */
.msg-mini-form .conv-rec-time { color: #f4f1ea; }
.msg-mini-form .conv-rec-hint { color: rgba(244,241,234,.55); }
.msg-mini-form .conv-rec-cancel { color: rgba(244,241,234,.7); }
.msg-mini-form .conv-rec-cancel:hover { background: rgba(229,72,77,.24); color: #ff8a8a; }
/* En las burbujas (360px de ancho) el aviso no cabe junto al cronómetro y la onda: se retira ahí. */
.msg-mini-form .conv-rec-hint, .nubia-mini .conv-rec-hint { display: none; }

/* Burbujas con imagen / audio — la descarga de estos 3 vive en el menú "⋮" (.bubble-menu), no superpuesta. */
.bubble-img { display: block; max-width: 100%; width: 240px; border-radius: 12px; margin-bottom: .25rem; cursor: zoom-in; }
/* ===== Nota de voz (reproductor propio, estilo WhatsApp) =====
   Sustituye al <audio controls> nativo, que no ofrece control de velocidad y no se puede vestir.
   Los colores se resuelven con dos variables locales para no repetir cada regla en las cuatro
   combinaciones (recibida/enviada × área grande/burbuja): --vn-on es lo ya reproducido y el botón;
   --vn-off, lo que falta. */
.vnote { --vn-on: var(--accent); --vn-off: rgba(28,26,24,.22);
  display: flex; align-items: center; gap: .5rem; width: 258px; max-width: 100%; margin-bottom: .25rem; }
.vnote-play { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%; border: none; cursor: pointer;
  background: var(--vn-on); color: #fff; display: grid; place-items: center; transition: transform .15s var(--ease-out), filter .15s; }
.vnote-play:hover { filter: brightness(1.07); }
.vnote-play:active { transform: scale(.93); }
.vnote-play .app-icon { width: 15px; height: 15px; }
/* La onda es el control de avance: se puede tocar en cualquier punto para saltar ahí. */
.vnote-track { flex: 1 1 auto; min-width: 0; height: 26px; display: flex; align-items: center; gap: 2px; cursor: pointer; }
.vnote-track i { display: block; flex: 1 1 auto; min-width: 2px; border-radius: 2px; background: var(--vn-off); transition: background .12s; }
.vnote-track i.is-on { background: var(--vn-on); }
/* Solo se mueve mientras suena; en pausa la onda queda quieta en su altura real. */
.vnote.is-playing .vnote-track i { animation: vnoteWave 1.1s ease-in-out infinite; }
.vnote.is-playing .vnote-track i:nth-child(3n+1) { animation-delay: .12s; }
.vnote.is-playing .vnote-track i:nth-child(3n+2) { animation-delay: .26s; }
@keyframes vnoteWave { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(.55); } }
.vnote-time { flex: 0 0 auto; font-size: .68rem; font-variant-numeric: tabular-nums; color: var(--ink-soft); min-width: 28px; text-align: right; }
.vnote-speed { flex: 0 0 auto; min-width: 30px; padding: .15rem .34rem; border-radius: 100px; border: 1px solid var(--vn-off);
  background: transparent; color: var(--ink-soft); font: 600 .66rem/1.4 var(--sans); cursor: pointer; transition: background .15s, color .15s, border-color .15s; }
.vnote-speed:hover, .vnote-speed.is-fast { background: var(--vn-on); border-color: var(--vn-on); color: #fff; }
/* Burbuja propia (fondo coral en las burbujas flotantes, oscuro en el área grande): la misma paleta
   dejaría el coral sobre coral, así que ahí se invierte a blanco. */
.bubble.mine .vnote { --vn-on: #fff; --vn-off: rgba(255,255,255,.4); }
.bubble.mine .vnote-play { color: var(--accent-deep); }
.bubble.mine .vnote-time { color: rgba(255,255,255,.75); }
.bubble.mine .vnote-speed { color: rgba(255,255,255,.85); }
.bubble.mine .vnote-speed:hover, .bubble.mine .vnote-speed.is-fast { color: var(--accent-deep); }
/* En las burbujas flotantes hay menos ancho disponible. */
.msg-mini-body .vnote, .nubia-mini-body .vnote { width: 216px; }
@media (prefers-reduced-motion: reduce) {
  .vnote.is-playing .vnote-track i { animation: none; }
  .conv-rec-wave i { animation: none; transform: scaleY(.7); }
}
.bubble-video { display: block; width: 260px; max-width: 100%; border-radius: 12px; margin-bottom: .25rem; background: #000; }
.bubble.mine .bubble-img, .bubble.mine .bubble-video { margin-left: auto; }
.bubble-file { display: inline-flex; align-items: center; gap: .5rem; max-width: 260px; padding: .55rem .7rem; border-radius: 12px; background: var(--paper); border: 1px solid var(--line-2); color: var(--ink-soft); margin-bottom: .25rem; text-decoration: none; }
.bubble-file span { flex: 1 1 auto; min-width: 0; font-size: .82rem; font-weight: 500; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bubble-file .app-icon { width: 16px; height: 16px; flex: 0 0 auto; color: var(--accent-deep); }
.bubble.mine .bubble-file { background: rgba(255,255,255,.16); border-color: rgba(255,255,255,.28); color: #fff; }
.bubble.mine .bubble-file span, .bubble.mine .bubble-file .app-icon { color: #fff; }
/* Mientras se firma la URL de Storage del adjunto (ver hydrateAttachments en app.js) */
.bubble-att-loading { display: inline-flex; align-items: center; gap: .5rem; padding: .55rem .7rem; border-radius: 12px; background: var(--paper); border: 1px solid var(--line-2); color: var(--ink-mute); margin-bottom: .25rem; font-size: .82rem; }
.bubble-att-loading .app-icon { width: 16px; height: 16px; flex: 0 0 auto; opacity: .6; }
.bubble.mine .bubble-att-loading { background: rgba(255,255,255,.16); border-color: rgba(255,255,255,.28); color: #fff; }
.bubble a { color: inherit; text-decoration: underline; overflow-wrap: break-word; } /* excepción: enlaces */
.bubble:has(.bubble-img), .bubble:has(.vnote), .bubble:has(.bubble-video), .bubble:has(.bubble-file), .bubble:has(.bubble-att-loading), .bubble:has(.bubble-doc) { padding: .45rem; }

/* Chip de documento estilo Claude — preview sobre el input del chat de Nubia. */
.asst-attach-preview { padding: .7rem 1.3rem 0; }
.doc-chip { display: inline-flex; align-items: center; gap: .6rem; max-width: 100%; padding: .5rem .7rem; border: 1px solid var(--line); border-radius: 12px; background: var(--paper); box-shadow: 0 1px 3px rgba(28,26,24,.06); }
.doc-chip-ico { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 8px; display: grid; place-items: center; background: var(--accent); color: #fff; }
.doc-chip-ico .app-icon { width: 17px; height: 17px; }
.doc-chip-meta { min-width: 0; display: flex; flex-direction: column; }
.doc-chip-meta b { font-size: .82rem; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }
.doc-chip-meta span { font-size: .7rem; color: var(--ink-mute); }
.doc-chip-x { flex: 0 0 auto; width: 22px; height: 22px; border: 0; background: var(--bg-3); border-radius: 50%; display: grid; place-items: center; cursor: pointer; color: var(--ink-soft); transition: background .2s, color .2s; }
.doc-chip-x .app-icon { width: 13px; height: 13px; transform: rotate(45deg); }
.doc-chip-x:hover { background: var(--accent); color: #fff; }
/* Documento dentro de una burbuja enviada a Nubia. Atenuado/traslúcido, con aire respecto al texto. */
.bubble-doc { display: inline-flex; align-items: center; gap: .55rem; max-width: 260px; padding: .5rem .65rem; border-radius: 12px; background: rgba(28,26,24,.04); border: 1px solid var(--line-2); margin-bottom: .6rem; }
.bubble-doc-ico { flex: 0 0 auto; width: 30px; height: 30px; border-radius: 7px; display: grid; place-items: center; background: rgba(242,107,94,.16); color: var(--accent-deep); }
.bubble-doc-ico .app-icon { width: 15px; height: 15px; }
.bubble-doc-meta { min-width: 0; display: flex; flex-direction: column; }
.bubble-doc-meta b { font-size: .8rem; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 180px; }
.bubble-doc-meta span { font-size: .68rem; color: var(--ink-mute); }
.bubble.mine .bubble-doc { background: rgba(255,255,255,.1); border-color: rgba(255,255,255,.2); }
.bubble.mine .bubble-doc-ico { background: rgba(255,255,255,.2); color: #fff; }
.bubble.mine .bubble-doc-meta b { color: #fff; }
.bubble.mine .bubble-doc-meta span { color: rgba(255,255,255,.72); }

/* "Visto": va FUERA de la burbuja, como hermano suyo, para que quede pegado al borde derecho de la
   columna (el contenedor del chat es flex en columna) y no dentro del globo coral. Solo aparece bajo
   UNA burbuja — la del último mensaje propio que el otro alcanzó a ver.
   El margen superior NEGATIVO contrarresta el `gap` de la columna: sin él, el "Visto" flotaba a la
   misma distancia que separa dos mensajes y parecía suelto en vez de pertenecer a su burbuja.
   ⚠️ El tamaño se declara junto al resto de tamaños del portal (línea ~2222), NO acá: `.app-body *`
   lleva `font-size: 14px !important` y aplasta cualquier font-size de igual especificidad. */
/* En la burbuja flotante el globo propio lleva `margin-right: 10px`; el "Visto" lo copia para que
   los dos bordes derechos coincidan. */
/* ---------- Separador de día en el chat (29-jul-2026) ----------
   Al subir por la conversación, cada cambio de fecha deja una marca centrada y tenue. Es un
   subtexto, no un título: fondo suave, sin borde y sin caja alta. El tamaño se declara junto al
   resto de tamaños (línea ~2442), porque `.app-body *` lleva 14px !important. */
.chat-day { align-self: center; margin: .45rem 0 .15rem; }
.chat-day span { display: inline-block; padding: .2rem .75rem; border-radius: 100px; background: var(--bg-2); color: var(--ink-mute); }
.msg-mini-body .chat-day { margin: .3rem 0 .1rem; }

/* ---------- Buscador dentro de la conversación (29-jul-2026) ----------
   Lupa en la cabecera → barra bajo la cabecera con contador y flechas. La barra es hermana de
   .conv-body dentro de .msg-conv (flex en columna), así que no le roba alto al chat: crece el
   contenedor y el cuerpo sigue con su propio scroll. */
.conv-head-actions { display: flex; align-items: center; gap: .5rem; }
.conv-find-open { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--line); background: var(--bg); color: var(--ink-soft); display: grid; place-items: center; cursor: pointer; transition: border-color .2s, color .2s, background .2s; }
.conv-find-open:hover, .conv-find-open.is-on { border-color: var(--accent); color: var(--accent-deep); }
/* Copia el formato de la barra de escribir (.conv-form): mismo alto de fila, campo en píldora con el
   mismo borde y relleno, y botones redondos .conv-tool — los mismos del micrófono y los adjuntos. */
.conv-find { flex: 0 0 auto; display: flex; align-items: center; gap: .6rem; padding: 1rem 1.3rem; border-bottom: 1px solid var(--line-2); }
.conv-find-field { flex: 1; min-width: 0; display: flex; align-items: center; gap: .55rem; padding: .7rem 1rem; border-radius: 20px; border: 1px solid var(--line); background: var(--bg); color: var(--ink-mute); }
.conv-find-field:focus-within { border-color: var(--accent); }
.conv-find-field input { flex: 1; min-width: 0; border: 0; background: none; outline: none; font: inherit; color: var(--ink); }
/* Fuera la ✕ azul que el navegador le pone solo a los `type="search"`: es del sistema, no del portal,
   y se cuela en azul dentro de una barra coral. Para limpiar está la ✕ redonda de la derecha.
   Mismo apaño que en el buscador de la barra superior (línea ~98). */
.conv-find-field input::-webkit-search-cancel-button,
.conv-find-field input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; display: none; }
.conv-find-count { flex: 0 0 auto; color: var(--ink-mute); font-variant-numeric: tabular-nums; white-space: nowrap; }
.conv-find-nav:disabled { opacity: .4; cursor: default; }
.conv-find-nav:disabled:hover { border-color: var(--line); color: var(--ink-soft); }
.conv-find-nav--up .app-icon { transform: rotate(180deg); }
.conv-find-x { line-height: 1; }
/* Coincidencias: todas marcadas en coral suave; la que se está mirando, en coral pleno. */
.chat-hit { background: rgba(242,107,94,.24); color: inherit; border-radius: 3px; padding: 0 1px; }
.chat-hit.is-current { background: var(--accent); color: #fff; }
.bubble.mine .chat-hit { background: rgba(255,255,255,.28); }
.bubble.mine .chat-hit.is-current { background: #fff; color: var(--accent-deep); }
.bubble.is-found { box-shadow: 0 0 0 2px rgba(242,107,94,.5); }

/* Pie de burbuja: hora + menú "⋮" (descargar/editar/eliminar) */
.bubble-foot { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-top: .25rem; }
.bubble-foot time { margin-top: 0; }
.bubble-menu { position: relative; }
.bubble-menu-btn { width: 20px; height: 20px; border: none; background: none; padding: 0; display: grid; place-items: center; color: inherit; opacity: .55; cursor: pointer; border-radius: 6px; transition: opacity .15s, background .15s; }
.bubble-menu-btn:hover { opacity: 1; background: rgba(28,26,24,.08); }
.bubble.mine .bubble-menu-btn:hover { background: rgba(255,255,255,.18); }
.bubble-menu-btn .app-icon { width: 15px; height: 15px; }
.bubble-menu-panel { position: absolute; bottom: calc(100% + 4px); z-index: 5; min-width: 150px; padding: .35rem; border-radius: 12px; background: var(--paper); border: 1px solid var(--line-2); box-shadow: 0 12px 30px -10px rgba(28,26,24,.35); display: flex; flex-direction: column; gap: .1rem; }
.bubble.mine .bubble-menu-panel { right: 0; }
.bubble:not(.mine) .bubble-menu-panel { left: 0; }
.bubble-menu-item { display: flex; align-items: center; gap: .5rem; padding: .45rem .6rem; border-radius: 8px; border: none; background: none; font: inherit; font-size: .82rem; color: var(--ink); text-decoration: none; text-align: left; cursor: pointer; white-space: nowrap; }
.bubble-menu-item:hover { background: var(--bg); }
.bubble-menu-item .app-icon { width: 15px; height: 15px; flex: 0 0 auto; color: var(--ink-mute); }
.bubble-menu-item--danger { color: #b3261e; }
.bubble-menu-item--danger .app-icon { color: #b3261e; }
.bubble-menu-item--danger:hover { background: #fdecea; }

/* Edición inline de un mensaje propio (primer minuto) */
/* Editor DENTRO de la burbuja (11-ago-2026). Era una fila con un `input` de una línea; ahora es un
   textarea que crece y las acciones van debajo, para que un mensaje largo se lea entero mientras se
   corrige. Sin contador de segundos: el reloj se pausa mientras el editor está abierto. */
/* ---------- Tarjeta de video en el chat (12-ago-2026) ----------
   Un enlace de Loom o YouTube se leía como una línea azul más y el alumno no abría el video. La
   tarjeta va DEBAJO del texto, sin quitar el enlace, y se nota de un vistazo que hay algo que ver.
   Horizontal y compacta a propósito: la burbuja es estrecha y una portada grande empujaría la
   conversación entera. */
.vcard { display: flex; align-items: center; gap: .7rem; margin-top: .5rem; padding: .45rem;
  border-radius: 11px; text-decoration: none; position: relative;
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.22); color: inherit;
  transition: background .18s, border-color .18s; }
.vcard:hover { background: rgba(255,255,255,.2); border-color: rgba(255,255,255,.4); }
/* En la burbuja AJENA el fondo es claro, así que la tarjeta va sobre papel. */
.bubble:not(.mine) .vcard { background: var(--bg); border-color: var(--line); }
.bubble:not(.mine) .vcard:hover { background: var(--bg-2); border-color: var(--accent); }
.vcard-thumb { flex: 0 0 auto; width: 104px; aspect-ratio: 16 / 9; border-radius: 8px;
  background-color: var(--ink); background-size: cover; background-position: center; }
/* Sin miniatura pública (Loom, Vimeo): un fondo de marca en vez de un hueco gris. */
.vcard-thumb--liso { background-image: var(--grad-box); }
.vcard-play { position: absolute; left: 0; top: 50%; transform: translateY(-50%); margin-left: calc(.45rem + 34px);
  width: 30px; height: 30px; border-radius: 50%; background: rgba(28,26,24,.72); color: #fff;
  display: flex; align-items: center; justify-content: center; pointer-events: none; }
.vcard-play .app-icon { width: 14px; height: 14px; margin-left: 2px; }
.vcard-meta { display: flex; flex-direction: column; min-width: 0; }
.vcard-meta b { font-size: .85rem; font-weight: 600; }
.vcard-meta > span { font-size: .76rem; opacity: .7; }
@media (max-width: 560px) { .vcard-thumb { width: 84px; } .vcard-play { margin-left: calc(.45rem + 24px); width: 26px; height: 26px; } }

.bubble-edit-form { display: block; margin-top: .35rem; }
.bubble-edit-form textarea {
  width: 100%; min-width: min(58vw, 320px); box-sizing: border-box;
  font: inherit; font-size: .85rem; line-height: 1.45;
  background: rgba(255,255,255,.14); border: 1px solid rgba(255,255,255,.3); color: #fff;
  border-radius: 10px; padding: .42rem .6rem; resize: none; overflow-y: auto; max-height: 260px;
}
.bubble-edit-form textarea:focus { outline: none; border-color: rgba(255,255,255,.65); }
/* Solo se editan los mensajes PROPIOS, así que la burbuja siempre es la oscura y ese es el estilo
   base. Se deja igualmente el caso claro por si algún día se pudiera editar en otra superficie —
   cuesta una línea y evita texto blanco sobre blanco. */
.bubble:not(.mine) .bubble-edit-form textarea { background: var(--paper); border-color: var(--line); color: var(--ink); }
.bubble-edit-acciones { display: flex; justify-content: flex-end; gap: .4rem; margin-top: .4rem; }

/* ---------- Asistente IA "Nubia" ---------- */
.asst-chat { display: flex; flex-direction: column; min-height: 480px; max-height: 76vh; padding: 0; overflow: hidden; }
.asst-head { display: flex; align-items: center; gap: .85rem; padding: 1rem 1.3rem; border-bottom: 1px solid var(--line-2); }
.asst-avatar { width: 46px; height: 46px; border-radius: 50%; flex: 0 0 auto; background: var(--bg-2) center/cover no-repeat; box-shadow: inset 0 0 0 1px var(--line-2); }
.asst-id { flex: 1; min-width: 0; }
.asst-id b { font-size: 1rem; display: inline-flex; align-items: center; gap: .45rem; }
.asst-status { display: flex; align-items: center; gap: .42rem; font-size: .76rem; color: var(--ink-mute); margin-top: .15rem; }
.asst-status i { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: #1f7a4d; animation: livePulse 2.2s infinite; }
.asst-head .btn-xs { flex: 0 0 auto; }
.asst-body { flex: 1; }
.asst-typing { background: var(--bg); align-self: flex-start; padding: .8rem 1rem; }
.asst-typing .typing { display: inline-flex; gap: 5px; align-items: center; }
.asst-typing .typing i { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-mute); opacity: .45; animation: asstDot 1s infinite; }
.asst-typing .typing i:nth-child(2) { animation-delay: .15s; }
.asst-typing .typing i:nth-child(3) { animation-delay: .3s; }
@keyframes asstDot { 0%, 100% { transform: translateY(0); opacity: .4; } 50% { transform: translateY(-3px); opacity: 1; } }
.asst-note { font-size: .72rem; color: var(--ink-mute); text-align: center; padding: .55rem 1rem .9rem; }

/* ---- Búsqueda en el feed (barra de resultados + comentarios) ---- */
/* Feed vacío: sin caja blanca, texto centrado (misma altura que antes, sin el borde/fondo de tarjeta).
   display:flex además de text-align:center para que quede centrado también si el mensaje ocupa más
   de una línea o el bloque crece de alto (no depender solo del centrado horizontal del texto). */
.feed-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 220px; padding: clamp(2rem, 5vw, 3rem) 1rem; text-align: center; color: var(--ink-mute); }
/* Ícono + título + siguiente paso. Sin tarjeta: el cliente quitó la caja blanca a propósito, así que
   esto vive sobre el fondo. El !important del título es para pasar por encima del 13/14px plano. */
.feed-empty-ico { width: 52px; height: 52px; border-radius: 50%; background: var(--accent-soft); color: var(--accent-deep); display: grid; place-items: center; flex: 0 0 auto; margin-bottom: .9rem; }
.feed-empty-ico .app-icon { width: 22px; height: 22px; }
.feed-empty b { display: block; font-size: 15px !important; color: var(--ink); margin-bottom: .3rem; }
.feed-empty p { margin: 0; max-width: 34ch; line-height: 1.5; }
.feed-searchbar { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; background: var(--accent-soft); color: var(--accent-deep); border-radius: 12px; padding: .7rem 1rem; margin-bottom: 1rem; font-size: .9rem; }
.feed-searchbar b { font-weight: 700; }
.feed-searchbar button { margin-left: auto; font-size: .8rem; font-weight: 600; color: var(--accent-deep); text-decoration: underline; }
/* Comentarios (formato tipo Facebook: avatar + burbuja redondeada + meta con hora/eliminar) */
.fp-comments-section { margin-top: .9rem; padding-top: .9rem; border-top: 1px solid var(--line-2); }
.fp-comments-list { display: flex; flex-direction: column; gap: .55rem; }
.fp-comments-list:empty { display: none; }
.fp-comment { display: flex; gap: .6rem; align-items: flex-start; }
.fp-comment-av { width: 30px; height: 30px; border-radius: 50%; flex: 0 0 auto; }
/* flex:1 hace falta desde que el cuerpo también contiene las respuestas y el cajón de responder: sin
   él se encoge al ancho de la burbuja y el hilo entero queda en una columna estrechísima. */
.fp-comment-body { min-width: 0; flex: 1 1 auto; }
/* ===== Comentarios plegados y en hilo (10-ago-2026) ===== */
.fp-comments-toggle { display: flex; align-items: center; gap: .6rem; width: 100%; padding: .2rem 0 .1rem;
  background: none; border: 0; cursor: pointer; text-align: left; }
.fp-faces { display: flex; flex: 0 0 auto; }
/* Las caras se solapan como en una lista de asistentes; el borde las separa del fondo de la tarjeta. */
.fp-faces .fp-face { width: 24px; height: 24px; border-radius: 50%; flex: 0 0 auto; font-size: .68rem;
  border: 2px solid var(--paper); margin-left: -8px; }
.fp-faces .fp-face:first-child { margin-left: 0; }
.fp-comments-more { color: var(--ink-mute); font-size: .84rem; font-weight: 600; }
.fp-comments-toggle:hover .fp-comments-more { color: var(--accent-deep); }
/* Las respuestas cuelgan del comentario, alineadas bajo su burbuja. Un solo nivel: no hay sangría
   sobre sangría (el JS cuelga toda respuesta del comentario raíz). */
.fp-replies { display: flex; flex-direction: column; gap: .55rem; }
.fp-replies:empty { display: none; }
.fp-replies:not(:empty) { margin-top: .55rem; padding-left: .9rem; border-left: 2px solid var(--line-2); }
.fp-reply-form { margin-top: .55rem; }
.fp-reply-form input { font-size: .92rem; }
.fp-comment-bubble { display: inline-block; background: var(--bg); border-radius: 14px; padding: .5rem .85rem; overflow-wrap: break-word; word-break: normal; max-width: 100%; }
.fp-comment-bubble b { margin-right: .4rem; }
.fp-comment-bubble span { color: var(--ink-soft); }
.fp-comment-meta { display: flex; gap: .9rem; margin: .25rem 0 0 .85rem; color: var(--ink-mute); font-size: .72rem; }
.fp-comment-meta button { color: var(--ink-mute); font-weight: 600; cursor: pointer; }
.fp-comment-meta button:hover { color: #c33; }
.fp-comment-form { display: flex; align-items: center; gap: .6rem; margin-top: .7rem; }
.fp-comment-form .side-avatar { width: 30px; height: 30px; flex: 0 0 auto; }
.fp-comment-form input { flex: 1 1 auto; min-width: 0; border: 1px solid var(--line-2); background: var(--bg); border-radius: 100px; padding: .55rem .95rem; outline: none; color: var(--ink); font: inherit; }
.fp-comment-form input:focus { border-color: var(--accent); background: var(--paper); }

/* ---- Modal de perfil de un miembro ---- */
.profile-modal { text-align: center; display: flex; flex-direction: column; align-items: center; gap: .35rem; }
.profile-modal-av { width: 76px; height: 76px; border-radius: 50%; font-size: 1.6rem; margin-bottom: .4rem; }
.profile-modal h3 { font-family: var(--serif); font-weight: 400; font-size: 1.5rem; }
.profile-modal-role { font-size: .82rem; color: var(--accent-deep); font-weight: 600; }
/* La bio de la tarjeta va JUSTIFICADA y CORTADA en 11 líneas —el tamaño máximo que el cliente
   aprobó por captura (4-ago-2026)—; si el texto sigue, la CSS deja el «…» y aparece «Ver más»,
   que lleva al perfil completo. Así la tarjeta nunca crece más allá de lo aprobado. */
.profile-modal-bio { font-size: .92rem; color: var(--ink-soft); margin: .3rem 0 .2rem; overflow-wrap: break-word; text-align: justify; display: -webkit-box; -webkit-line-clamp: 11; -webkit-box-orient: vertical; overflow: hidden; }
.profile-modal-more { border: 0; background: none; font: inherit; font-size: .84rem; font-weight: 600; color: var(--accent-deep); cursor: pointer; padding: 0; }
.profile-modal-more:hover { text-decoration: underline; }
.profile-modal-loc { font-size: .82rem; color: var(--ink-mute); display: inline-flex; align-items: center; gap: .35rem; }
/* Planes/grupos habilitados de un miembro (solo miembros, nunca equipo — ver openMemberProfile):
   mini-boxes centradas y apiladas, no píldoras en fila como en la tarjeta de Miembros. Diseño
   aprobado por el cliente vía preview (13-jul-2026). */
.profile-modal .section-mini-label { text-align: center; width: 100%; margin: .3rem 0 .3rem; }
.profile-modal-plans { list-style: none; padding: 0; margin: 0 0 .2rem; display: flex; flex-direction: column; align-items: center; gap: .5rem; width: 100%; }
.profile-modal-plans li { font-size: .78rem; color: var(--accent-deep); font-weight: 600; background: var(--accent-soft); border-radius: 100px; padding: .3rem .8rem; width: auto; text-align: center; }
.profile-modal .btn { margin-top: .4rem; }
/* "A quién ayudas" (fase2/68): va ENCIMA de la biografía porque es la frase que hace que alguien
   decida escribirte. Va después de .profile-modal-bio a propósito — comparte tipografía con ella y
   solo cambia el color; puesto antes, la regla base lo pisaría en silencio. */
.profile-modal-helps { font-size: .88rem; color: var(--accent-deep); font-weight: 600; margin: .1rem 0 0; overflow-wrap: break-word; }
/* Popup de un miembro del EQUIPO (coach/closer/admin/CEO): mismo degradado negro→coral que su
   tarjeta en el directorio (.member-card--dark), texto claro (pedido del cliente, 13-jul-2026).
   Los miembros regulares mantienen el fondo blanco por defecto. */
.app-modal-card.profile-modal-dark { background: var(--grad-portada); color: var(--paper); }
.app-modal-card.profile-modal-dark .profile-modal h3 { color: var(--paper); }
.app-modal-card.profile-modal-dark .profile-modal-role { color: var(--accent); }
.app-modal-card.profile-modal-dark .profile-modal-bio { color: rgba(244,241,234,.85); }
.app-modal-card.profile-modal-dark .profile-modal-loc { color: rgba(244,241,234,.72); }
.app-modal-card.profile-modal-dark .profile-modal-loc .app-icon { color: rgba(244,241,234,.9); }
.app-modal-card.profile-modal-dark .ceo-badge { background: var(--paper); color: var(--ink); }

/* ---- Filas de Mensajes con hora (WhatsApp) ---- */
.msg-search { display: flex; align-items: center; gap: .5rem; background: var(--bg); border: 1px solid var(--line); border-radius: 100px; padding: .55rem .9rem; margin-bottom: .7rem; color: var(--ink-mute); }
.msg-search input { border: 0; background: none; outline: none; font: inherit; font-size: .88rem; width: 100%; color: var(--ink); }
/* Ojo con el selector: `.mt-info span` (app.css:1325) pone display:block y max-width:180px a TODOS
   los span de la fila, y `.mt-top` a secas le pierde por especificidad aunque esté escrito después.
   Resultado (que venía de antes): la hora se descolgaba a su propia línea y la fila crecía un
   tercio. Se antepone .mt-info para empatar y ganar por orden. */
.mt-info .mt-top { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; max-width: none; }
/* El nombre se recorta con puntos suspensivos; la hora nunca se aplasta. Sin el min-width:0 el
   nombre se niega a encogerse y le come los dígitos a la hora («14:1…», visto en preview). */
.mt-info .mt-top b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mt-top time { font-size: .68rem; color: var(--ink-mute); flex: 0 0 auto; }
.mt-prev { display: block; font-size: .8rem; color: var(--ink-mute); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: .1rem; }
/* Los SVG de la plataforma son display:block, así que un icono dentro del adelanto se llevaba una
   línea entera para él y la fila crecía casi al doble. Le pasaba ya al candado de las
   conversaciones finalizadas; se vio al añadir la campana de los grupos de anuncios. */
.mt-prev .app-icon { display: inline-block; vertical-align: -3px; }
.msg-thread.is-active .mt-top time, .msg-thread.is-active .mt-prev { color: rgba(244,241,234,.7); }
/* Círculo tipo WhatsApp con el nº de mensajes sin leer (derecha de la fila) */
.mt-unread { margin-left: auto; flex: 0 0 auto; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 100px; background: var(--accent); color: #fff; font: 700 .72rem/1 var(--sans); display: inline-flex; align-items: center; justify-content: center; box-shadow: 0 1px 4px rgba(28,26,24,.28); }
/* Punto sin número: la conversación que YO dejé como no leída. Más pequeño que el globito porque
   no lleva cifra dentro — solo tiene que verse, no leerse (pedido del cliente, 31-jul-2026).
   Va DESPUÉS de .mt-unread: misma especificidad, así que si se pone antes gana la regla base y el
   punto sale del tamaño del globito. Es la trampa de cascada de siempre. */
.mt-unread--dot { width: 10px; height: 10px; min-width: 0; padding: 0; border-radius: 50%; font-size: 0; line-height: 0; box-shadow: none; }
.msg-thread.is-active .mt-unread { background: var(--paper); color: var(--accent); box-shadow: none; }
.mt-unread--mini { min-width: 19px; height: 19px; }

/* ---- Enlaces a perfil (avatar/nombre clicables) ---- */
.lnk { cursor: pointer; }
b.lnk:hover { color: var(--accent-deep); text-decoration: underline; text-underline-offset: 2px; }
span.lnk:hover { opacity: .85; }
.profile-posts-list { display: flex; flex-direction: column; gap: 1rem; margin-top: 1rem; }
.profile-cta { margin-top: 1.1rem; }
.profile-cta .btn { justify-content: center; }

/* ===== Categorías / equipo ===== */
.m-status.team { background: #e8e3f6; color: #5b46a8; }
.member-coach { font-size: .78rem; color: var(--ink-soft); display: flex; align-items: center; gap: .35rem; margin-top: .5rem; }
.member-coach .app-icon { width: 14px; height: 14px; color: var(--accent); }
/* Aviso de solicitudes pendientes en la tarjeta de miembro (admin) */
.member-card.has-req { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.manage-coach { display: inline-flex; align-items: center; gap: .35rem; font-size: .76rem; color: var(--accent); margin-top: .3rem; }
.manage-coach .app-icon { width: 13px; height: 13px; }
.role-switch { display: inline-flex; gap: .4rem; flex-wrap: wrap; }
.welcome-note { font-size: .76rem; color: var(--ink-mute); text-align: center; }
.welcome-note .app-icon { width: 13px; height: 13px; display: inline-block; vertical-align: -2px; }

/* ===== Mensajes: terminar / reabrir conversación ===== */
.conv-head-id { flex: 1; min-width: 0; }
.conv-head-actions { flex: 0 0 auto; }

/* ===== Tareas / submódulos expandibles ===== */
.task { border-radius: 10px; transition: background .2s; }
.task + .task { border-top: 1px solid var(--line-2); }
/* Hover de selección: el tono crema de siempre (var(--bg)), pero SOLO cuando la tarea está
   COLAPSADA — al desplegarse se retira por completo para no restarle contraste a los boxes
   internos (Documentos/Enlaces/Entrega), que usan ese mismo tono (pedido del cliente, 13-jul-2026:
   se probó un degradado oscuro y se descartó; se vuelve al efecto anterior manteniendo el
   :not(.is-expanded)). */
.task:not(.is-expanded):hover { background: var(--bg); }
.task-head { display: flex; align-items: center; gap: .9rem; padding: .8rem; cursor: pointer; }
.task-head .t { flex: 1; min-width: 0; }
.task-head .t b { font-size: .9rem; font-weight: 500; display: block; }
.task-head .t span { font-size: .74rem; color: var(--ink-mute); }
.task.done .t b { color: var(--ink-mute); }
.task.done .lesson-check { background: var(--accent); border-color: var(--accent); color: #fff; }
/* Tarea bloqueada por el avance secuencial (candado, sin contenido). El alumno la ve atenuada. */
.task.is-task-locked { opacity: .72; }
.task.is-task-locked .task-head { cursor: default; }
.task.is-task-locked .lesson-check { border-color: var(--line); color: var(--ink-mute); background: var(--bg-2); cursor: default; }
.task.is-task-locked .lesson-type { color: var(--ink-mute); }
.task.is-task-locked .t b { color: var(--ink-mute); }
.task.is-task-locked .t span { color: var(--accent-deep); font-weight: 600; }
.lesson-check.locked { opacity: .5; cursor: not-allowed; }
.task-chevron { transition: transform .3s var(--ease-out), color .2s; color: var(--ink-mute); flex: 0 0 auto; }
.task.is-expanded .task-chevron { transform: rotate(180deg); }
.task-del, .task-edit, .task-move { background: none; border: none; color: var(--ink-mute); cursor: pointer; flex: 0 0 auto; padding: .2rem; border-radius: 6px; }
.task-del .app-icon, .task-edit .app-icon, .task-move .app-icon { width: 16px; height: 16px; }
.task-del:hover { color: #b3402f; background: #f7ecea; }
.task-edit:hover, .task-move:hover { color: var(--accent); background: rgba(230,113,90,.12); }
/* Encendido: la lista está en modo mover. Se distingue en coral, como el resto de lo activo. */
.task-move.is-on { color: var(--accent-deep); background: var(--accent-soft); }
.task-body { display: none; padding: 0 .8rem 1rem 3.4rem; }
.task.is-expanded .task-body { display: block; animation: taskOpen .25s var(--ease-out); }
@keyframes taskOpen { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
/* Al 75% de su ancho anterior (antes ocupaba el 100% del ancho de la tarea); el botón de pantalla
   completa (.task-fs) sigue disponible para verlo grande una vez iniciado. */
.task-video { position: relative; aspect-ratio: 16 / 9; background: var(--ink); border-radius: 12px; overflow: hidden; display: grid; place-items: center; width: 75%; }
.task-video-el { width: 100%; height: 100%; object-fit: contain; background: #000; }
/* Video Loom/YouTube: se incrusta al abrir la tarea (carga diferida). Mientras carga (frame vacío),
   un spinner tenue sobre el fondo oscuro; al aparecer el iframe, el :empty deja de aplicar y se va. */
.task-video--lazy:empty::after { content: ""; width: 30px; height: 30px; border: 3px solid rgba(255,255,255,.22); border-top-color: #fff; border-radius: 50%; animation: taskVideoSpin .8s linear infinite; }
@keyframes taskVideoSpin { to { transform: rotate(360deg); } }
.task-fs { position: absolute; top: .6rem; right: .6rem; width: 32px; height: 32px; border-radius: 8px; border: none; background: rgba(0,0,0,.45); color: #fff; cursor: pointer; display: grid; place-items: center; }
.task-fs .app-icon { width: 16px; height: 16px; }
.task-fs:hover { background: rgba(0,0,0,.7); }
.task-content { margin: .9rem 0; font-size: .85rem; color: var(--ink-soft); line-height: 1.55; }
.task-content p { margin: 0 0 .6rem; }
.task-content p:last-child { margin-bottom: 0; }
.task-content p.task-h { font-weight: 600; color: var(--ink); margin-top: .9rem; }
.task-content strong { color: var(--ink); font-weight: 600; }
.task-content ul, .task-content ol { margin: .2rem 0 .7rem; padding-left: 1.4rem; }
.task-content li { margin: .15rem 0; }
/* Viñetas y números de VERDAD, solo dentro del formato nuevo (.task-rich) y del editor. styles.css
   los quita en todo el portal (`ul { list-style: none }`), así que el botón de viñetas no marcaría
   nada. Se acota a estas dos cajas para no cambiar cómo se ve el contenido que ya está publicado. */
.task-rich ul, .rt-area ul { list-style: disc; }
.task-rich ol, .rt-area ol { list-style: decimal; }
.task-rich ul, .task-rich ol { margin: .2rem 0 .7rem; padding-left: 1.4rem; }
/* Cuerpo con formato: la imagen va incrustada en el texto y al pulsarla se abre en vista previa. */
.task-content img.rt-img { max-width: 100%; height: auto; border-radius: 10px; display: block; margin: .6rem 0; cursor: zoom-in; }
.task-content .task-doc { margin: .6rem 0; }
/* Vista previa de una imagen del contenido: capa a pantalla completa, por encima de las pestañas
   emergentes (z-index 200) y por debajo del aviso flotante (300). */
.img-viewer { position: fixed; inset: 0; z-index: 260; display: flex; align-items: center; justify-content: center; padding: 3.2rem 1.2rem; background: rgba(28,26,24,.86); backdrop-filter: blur(3px); animation: viewIn .25s var(--ease-out); }
/* La pestaña emergente donde vive la foto (10-ago-2026). Tiene TAMAÑO PROPIO, y ahí está la gracia:
   una imagen grande se reduce hasta caber dentro, y una pequeña se queda a su tamaño real, centrada,
   con el margen oscuro alrededor — el comportamiento de Facebook.
   Antes la imagen colgaba suelta del fondo con `max-height: 100%`; ese porcentaje no tenía contra qué
   resolverse (la fila del grid se ajustaba al contenido, que es lo que se quería medir), así que no
   se aplicaba y una foto alta se salía de la pantalla por abajo. */
/* `minmax(0, 1fr)` NO es decorativo: sin él la fila del grid se ajusta a la imagen, el `max-height:
   100%` de abajo no tiene contra qué resolverse y se ignora en silencio. Medido: una foto de
   2000x2600 salía a 1040x1352 dentro de un recuadro de 640 de alto. Con esto, 492x640. Es la misma
   trampa de siempre en este proyecto. */
/* Sin recuadro oscuro (11-ago-2026, pedido del cliente): el envoltorio ya no tiene tamaño ni fondo
   propios, se ciñe a la imagen. Así una foto vertical no queda con dos franjas negras a los lados.
   El tope de tamaño se mudó a la propia imagen, que es quien manda ahora. */
.iv-box { display: block; background: none; }
/* Los topes van en píxeles y unidades de ventana A PROPÓSITO, nunca en porcentaje: un `max-height:
   100%` aquí no tendría contra qué resolverse (el padre ya no tiene alto propio) y el navegador lo
   ignoraría EN SILENCIO — es la trampa que en agosto sacó una foto de 2000x2600 a 1040x1352 dentro de
   un hueco de 640. El tercer valor descuenta el margen del fondo (3.2rem + 1.2rem) para que en una
   ventana baja la foto tampoco se salga por abajo.
   `width/height: auto` mantiene que una foto pequeña NO se agrande: se ve a su tamaño real.
   Y como en todo el visor: no se puede seleccionar ni arrastrar, y en el teléfono la pulsación larga
   no abre el menú de «guardar imagen». El clic derecho lo anula el JS. */
.iv-box img { display: block; width: auto; height: auto;
  max-width: min(92vw, 1040px, calc(100vw - 2.8rem));
  max-height: min(80vh, 820px, calc(100vh - 7rem));
  border-radius: 12px; box-shadow: 0 40px 90px -30px rgba(0,0,0,.7);
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
/* Pasar entre las imágenes de una publicación. Solo existe el chevron hacia abajo: se gira. */
.iv-nav { position: absolute; top: 50%; transform: translateY(-50%); width: 42px; height: 42px; border-radius: 50%;
  border: none; background: rgba(255,255,255,.9); color: var(--ink); display: grid; place-items: center; cursor: pointer; }
.iv-nav .app-icon { width: 20px; height: 20px; }
.iv-prev { left: 1rem; } .iv-prev .app-icon { transform: rotate(90deg); }
.iv-next { right: 1rem; } .iv-next .app-icon { transform: rotate(-90deg); }
.iv-count { position: absolute; bottom: 1.2rem; left: 50%; transform: translateX(-50%); color: #fff; font-size: .82rem; opacity: .82; }
.img-viewer-x { position: absolute; top: 1rem; right: 1rem; width: 36px; height: 36px; border-radius: 50%; border: none; background: rgba(255,255,255,.92); color: var(--ink); display: grid; place-items: center; cursor: pointer; }
.img-viewer-x .app-icon { width: 17px; height: 17px; transform: rotate(45deg); }
/* Entrega del alumno (sube archivo / escribe respuesta; el coach la ve) */
/* El box de la entrega usa el MISMO fondo y borde que los de enlace y documento (.task-doc): antes
   iba en coral y destacaba como si fuera un aviso. El coral se conserva solo en el ícono y el título
   de la cabecera, igual que hace la tarjeta de documento. */
.task-submit { margin: .9rem 0 .2rem; background: var(--paper); border: 1px solid var(--line-2); border-radius: 12px; padding: .8rem .9rem; }
.task-submit-h { display: flex; align-items: center; gap: .4rem; font-size: .78rem; font-weight: 600; color: var(--accent-deep); margin-bottom: .55rem; }
.task-submit-h .app-icon { width: 15px; height: 15px; }
.task-submit-empty { font-size: .82rem; color: var(--ink-soft); margin: 0 0 .6rem; }
.task-submit-current { display: flex; flex-direction: column; gap: .45rem; margin-bottom: .65rem; }
.task-submit-text { font-size: .85rem; color: var(--ink); font-style: italic; margin: 0; background: var(--paper); border: 1px solid var(--line-2); border-radius: 8px; padding: .5rem .65rem; }
.task-submit-meta { font-size: .72rem; color: var(--ink-mute); }
.task-submit-form { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.task-submit-file { display: inline-flex; align-items: center; gap: .35rem; font-size: .8rem; font-weight: 500; background: var(--paper); border: 1px dashed var(--accent); color: var(--accent-deep); border-radius: 8px; padding: .45rem .7rem; cursor: pointer; }
.task-submit-file .app-icon { width: 15px; height: 15px; }
.task-submit-area { flex: 1 1 220px; min-width: 180px; font: inherit; font-size: .85rem; border: 1px solid var(--line); border-radius: 8px; padding: .5rem .6rem; resize: vertical; background: var(--paper); color: var(--ink); }
.task-req { margin-top: .6rem; }
.task-req.coach, .task-req.video { background: var(--bg); border: 1px solid var(--line-2); border-radius: 10px; padding: .8rem .9rem; }
.task-req.coach p, .task-req.video p { font-size: .82rem; color: var(--ink-soft); line-height: 1.5; margin: 0 0 .6rem; }
.task-req p .app-icon { width: 15px; height: 15px; color: var(--accent); display: inline-block; vertical-align: -3px; margin-right: .35rem; }
.task-req-actions { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; }
/* 3.3 · Sesión estratégica ya agendada: botón atenuado, no invita a reagendar. */
.btn.is-scheduled { opacity: .7; cursor: default; color: #1f7a4d; border-color: rgba(31,122,77,.4); background: rgba(31,122,77,.08); }
.btn.is-scheduled:hover { background: rgba(31,122,77,.08); }
.task-status { font-size: .76rem; color: var(--ink-mute); }
.task-status.ok { color: #1f7a4d; }
.task-done-date { display: block; width: 100%; font-size: .72rem; color: var(--ink-mute); margin-top: .2rem; }
.task-done-date[hidden] { display: none; }
.task-add { width: 100%; margin-top: .6rem; padding: .6rem; border: 1px dashed var(--line); background: none; border-radius: 10px; color: var(--ink-soft); font-size: .82rem; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: .4rem; }
.task-add .app-icon { width: 15px; height: 15px; }
.task-add:hover { border-color: var(--accent); color: var(--accent); }
.modules-edit-note { display: flex; align-items: center; gap: .45rem; font-size: .82rem; color: var(--accent); background: #fbeee9; border: 1px solid #f3d6cc; border-radius: 10px; padding: .6rem var(--card-pad); margin-bottom: 1.1rem; }
.modules-edit-note .app-icon { width: 15px; height: 15px; flex: 0 0 auto; }
/* El texto va en un <span> (ver app.js): con el nota en flex, los nodos sueltos y los <b> serían flex
   items independientes y en móvil se parten en columnas ilegibles. Con 2+ líneas el ícono va arriba. */
.modules-edit-note { align-items: flex-start; }
.modules-edit-note > span { min-width: 0; }

/* ===== Pestañas emergentes de DOS COLUMNAS (28-jul-2026, aprobado por el cliente) =====
   Para los formularios largos: a la izquierda los datos, a la derecha lo que se consulta en lista
   (miembros, accesos) o la configuración secundaria (portada, audiencia). Así entran sin scroll. */
/* ⚠️ La tarjeta NO scrollea: scrollean sus columnas por dentro (28-jul-2026). Con la lista de miembros
   larga, la tarjeta crecía hasta el tope de 90vh y el botón de guardar quedaba bajo el pliegue, así
   que había que bajar para llegar a «Crear comunidad». Ahora la tarjeta es una columna flex acotada:
   cabecera y pie fijos, y el bloque del medio reparte lo que sobra. */
.app-modal-card.modal-split { max-width: 980px; width: min(980px, 96vw);
  display: flex; flex-direction: column; overflow: hidden; }
.modal-split > .manage-head, .modal-split > .modal-title, .modal-split > .app-modal-x { flex: 0 0 auto; }
/* `align-items: stretch` es lo que hace que la columna derecha llegue SIEMPRE hasta abajo, a ras de
   la línea de los botones, sin importar cuánto mida el formulario de la izquierda. */
.modal-split .msplit { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 370px); gap: 1.5rem;
  align-items: stretch; flex: 1 1 auto; min-height: 0; }
/* La columna izquierda scrollea sola si el formulario es más alto que la ventana (nunca la tarjeta). */
.modal-split .msplit > div:first-child { min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding-right: .2rem; }
.msplit-foot { flex: 0 0 auto; display: flex; gap: .5rem; flex-wrap: wrap; margin-top: 1.2rem; padding-top: 1.1rem; border-top: 1px solid var(--line-2); }
/* El formulario de evento envuelve todo: tiene que ser flex igual, o el pie no se queda abajo. */
.modal-split form { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
/* Variante A · con caja de color: cuando la derecha es una LISTA (miembros, accesos). */
.msplit-side { background: var(--bg); border: 1px solid var(--line-2); border-radius: 14px; padding: .9rem;
  display: flex; flex-direction: column; min-height: 0; }
.msplit-side > h4 { font-size: .84rem; font-weight: 600; margin-bottom: .1rem; }
.msplit-side .m-note { font-size: .74rem; }
/* Variante B · sin caja, solo una línea tenue: cuando la derecha son CAMPOS (evento). */
.msplit-line { border-left: 1px solid var(--line-2); padding-left: 1.5rem; display: flex; flex-direction: column; min-height: 0; }
.msplit-line h4 { font-size: .84rem; font-weight: 600; }
.msplit-hr { border: 0; border-top: 1px solid var(--line-2); margin: 1rem 0; }
/* `flex: 1 1 auto` + `min-height: 0` = la lista ocupa lo que sobra y scrollea ella sola. */
.msplit-list { margin-top: .6rem; flex: 1 1 auto; min-height: 90px; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: .3rem; padding-right: .2rem; }
/* Pertenecer o no NO tiñe la fila: lo único que cambia es el botón (pedido del cliente). */
.msplit-list .manage-row { padding: .78rem .85rem; gap: .75rem; border-radius: 13px; background: var(--paper); border: 1px solid var(--line-2); }
/* Lo que SÍ tiñe la fila es la categoría: el EQUIPO (coach/closer/admin/CEO) lleva el mismo degradado
   negro→coral que su tarjeta en el directorio de Miembros (.member-card--dark) y que su ficha
   emergente (.profile-modal-dark). Los alumnos se quedan en blanco (29-jul-2026). */
.msplit-list .manage-row.is-team { background: var(--grad-portada); border-color: transparent; color: var(--paper); }
.msplit-list .manage-row.is-team b { color: var(--paper); }
.msplit-list .manage-row.is-team .m-note { color: rgba(244,241,234,.72); }
/* Si el usuario tiene foto, su `background-image` va en línea y le gana a esta regla: la foto se ve
   igual y este color solo pinta el círculo de la inicial. */
.msplit-list .manage-row.is-team .side-avatar { background: rgba(244,241,234,.2); color: var(--paper); }
/* Los dos botones se adaptan al fondo oscuro conservando su semántica: «Agregar» sigue siendo el
   sólido (ahora en blanco, porque el coral cae justo sobre el tramo coral del degradado y se perdía)
   y «Quitar» sigue siendo el discreto, ahora traslúcido sobre el degradado. */
.msplit-list .manage-row.is-team .btn--coral { background: var(--paper); color: var(--ink); }
.msplit-list .manage-row.is-team .btn--coral:hover { background: #fff; box-shadow: 0 0 0 2px rgba(244,241,234,.35); }
.msplit-list .manage-row.is-team .btn--inbox { background: rgba(244,241,234,.16); color: var(--paper); border-color: rgba(244,241,234,.38); }
.msplit-list .manage-row.is-team .btn--inbox:hover { background: rgba(244,241,234,.26); border-color: rgba(244,241,234,.65); color: var(--paper); }
.msplit-sub { font-size: .72rem; font-weight: 600; color: var(--ink-mute); margin: .7rem 0 -.2rem; }
.msplit-sub:first-child { margin-top: .1rem; }
/* En una columna de 310px el nombre se partía en dos renglones cuando la fila llevaba la píldora de
   upsell. El nombre se queda en UNA línea (con puntos suspensivos si no cabe) y las acciones se
   apilan a la derecha, que es más ordenado que dejarlas envolver. */
.msplit-list .manage-row > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msplit-list .plan-row-actions { flex-direction: column; align-items: flex-end; gap: .3rem; flex: 0 0 auto; }
/* Filas compactas: con el avatar de 38px cada miembro medía ~85px y nueve llenaban la pantalla. */
.msplit-list .side-avatar { width: 38px; height: 38px; font-size: 14px !important; }
/* Nombre y subtítulo con puntos suspensivos: sin esto se cortaban a mitad de palabra ("Carmen Di
   Girolan"), que se lee como un error y no como un recorte. */
.msplit-list .manage-row b, .msplit-list .manage-row .m-note { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ⚠️ El `!important` NO es decorativo: `.app-body * { font-size: 14px !important }` (línea ~2307)
   le gana a cualquier regla sin !important, por específica que sea. Sin esto, el nombre y el
   subtítulo salían los dos a 14px — de ahí que el nombre no cupiera y las dos líneas se vieran
   apretadas una contra otra. Con tamaños propios y algo de aire, cada ficha respira (28-jul-2026). */
/* El nombre va al MISMO tamaño que el resto de los textos de la pestaña (14px, el del portal) y el
   subtexto conserva su proporción respecto al nombre: 11.5/13 ≈ 0,885 → 14 × 0,885 = 12,4px. */
.msplit-list .manage-row b { font-size: 14px !important; line-height: 1.35; }
.msplit-list .manage-row .m-note { font-size: 12.4px !important; line-height: 1.35; margin-top: .14rem; }
.msplit-sub { font-size: 12px !important; }
/* Nada de cursivas dentro de las pestañas emergentes (regla del cliente, 28-jul-2026): el subtexto
   bajo el nombre —y cualquier otra nota de un modal— va en redonda. La cursiva se reserva para las
   piezas de marca (la inicial del avatar, los números de módulo) y para las CITAS textuales
   (el motivo de una pausa, la respuesta de una entrega), que sí son otra voz. */
.app-modal-card .m-note { font-style: normal; }

/* La portada aprovecha el aire que dejó la lista acotada: cuadro más grande y botón debajo, no al
   lado (el cliente lo autorizó expresamente para cuadrar los espacios). */
.modal-split .evt-cover-upload { flex-direction: column; align-items: stretch; gap: .55rem; }
.modal-split .evt-cover-preview { width: 100%; height: 150px; }
.modal-split .evt-cover-up-actions { flex-direction: row; flex-wrap: wrap; align-items: center; }
/* Botón "ya está adentro": del color de la caja, para distinguirlo del coral de "agregar". */
.btn--inbox { background: var(--bg); color: var(--ink-soft); border: 1px solid var(--line); }
.btn--inbox:hover { border-color: var(--ink-mute); color: var(--ink); }
/* En pantallas chicas las dos columnas se apilan: la derecha pasa abajo, sin caja lateral ni línea. */
@media (max-width: 860px) {
  .modal-split .msplit { grid-template-columns: minmax(0, 1fr); }
  .msplit-line { border-left: 0; padding-left: 0; border-top: 1px solid var(--line-2); padding-top: 1.1rem; }
  .msplit-list { max-height: 320px; }
}

/* ===== Modal grande de creación de tarea ===== */
/* Los anchos suben en la misma proporción que el modal base (28-jul-2026): 680 → 820. */
.app-modal-card.modal-wide { max-width: 820px; width: min(820px, 94vw); }
/* Editor de módulo: ya era el más ancho (900); sube a 1000, tope razonable para no partir la lectura. */
.app-modal-card.modal-mod-edit { max-width: 1000px; width: min(1000px, 96vw); }
/* Gestionar miembro: 500 → 640. Es de los que más lo pedían (planes + comunidades + rol + coach). */
.app-modal-card.manage-modal { max-width: 640px; width: min(640px, 94vw); }
/* ---- Bienvenida (ver .welcome-card más arriba): un poco más ancho que el modal por defecto
   (440px) para que la primera oración del mensaje quepa en una sola línea. ---- */
.app-modal-card.welcome-modal { max-width: 580px; width: min(580px, 94vw); }
/* ---- Panel de upsell (miembro Premium · "+") ---- */
.app-modal-card.upsell-modal { max-width: 660px; width: min(660px, 94vw); padding: 0; overflow: hidden; }
.upsell-panel { position: relative; }
/* Cabecera con la FOTO de Nubia y el degradado negro→coral encima: mismo tratamiento que el hero de
   la portada (.hero-bg en styles.css), con la foto anclada a la derecha para que el titular respire
   sobre la zona oscura de la izquierda. Elegido por el cliente entre tres propuestas (29-jul-2026). */
.upsell-hero { position: relative; color: var(--paper); padding: 1.7rem 1.8rem 1.5rem;
  min-height: 208px; display: flex; flex-direction: column; justify-content: flex-end;
  background-image: linear-gradient(96deg, rgba(20,18,16,.95) 0%, rgba(20,18,16,.86) 30%, rgba(92,50,43,.55) 62%, rgba(168,74,62,.18) 100%), url("assets/img/nubia-upsell.webp");
  background-size: cover; background-position: center right; }
.upsell-badge { display: inline-flex; align-items: center; gap: .35rem; font-size: .68rem; font-weight: 600; background: rgba(244,241,234,.16); padding: .3rem .7rem; border-radius: 100px; margin-bottom: .8rem; align-self: flex-start; }
.upsell-badge .app-icon { width: 13px; height: 13px; }
/* Titular "de revista", el mismo patrón que "Diste el primer paso" (.welcome-card h3): primera línea
   en claro y la segunda en cursiva coral. El `font-size: inherit !important` del <em> NO es opcional:
   `.app-body *` fuerza 14px a todo y sin eso la cursiva sale diminuta. */
.upsell-hero h2 { font-family: "Fraunces", Georgia, "Times New Roman", serif !important; font-weight: 300 !important; font-size: clamp(1.5rem, 6vw, 2.05rem) !important; line-height: 1.03; letter-spacing: -0.02em; color: var(--paper); max-width: 12ch; }
.upsell-hero h2 em { font-family: "Fraunces", Georgia, "Times New Roman", serif !important; font-style: italic; font-weight: 400 !important; font-size: inherit !important; color: #FFB9A8; }
.upsell-body { padding: 1.4rem 1.8rem 0; }
.upsell-intro { font-size: 1rem; font-weight: 600; color: var(--ink); line-height: 1.4; margin-bottom: 1rem; }
.upsell-points { display: grid; gap: .55rem; margin: 1.1rem 0; }
.upsell-points li { display: flex; gap: .5rem; font-size: .88rem; color: var(--ink-soft); line-height: 1.4; }
.upsell-points .app-icon { width: 17px; height: 17px; color: #1f7a4d; flex: 0 0 auto; margin-top: 1px; }
.upsell-tease { font-size: .9rem; color: var(--ink); }
.upsell-cta { margin-top: 1.3rem; padding: 1.2rem 1.9rem 1.9rem; border-top: 1px solid var(--line-2); text-align: center; }
.upsell-cta p { font-size: .88rem; color: var(--ink-soft); margin-bottom: .75rem; }
.upsell-cta .btn { width: 100%; max-width: 300px; }
.upsell-confirm { text-align: center; padding: 2.2rem 1.8rem 2rem; position: relative; }
.upsell-confirm .upsell-ok { background: #e2f4ea; color: #1f7a4d; border-color: #c6e7d4; }
.upsell-confirm h2 { font-family: var(--serif); font-weight: 300; font-size: 1.5rem; margin-top: 1rem; }
.upsell-confirm p { color: var(--ink-soft); margin: .7rem auto 1.3rem; max-width: 40ch; }
.upsell-confirm .btn { min-width: 200px; }
/* el "+" en modo upsell se distingue con el coral de marca */
.plan-tab--upsell { border-style: solid; border-color: var(--accent); color: var(--accent-deep); background: var(--accent-soft); }
.plan-tab--upsell:hover { background: var(--accent); color: #fff; border-color: var(--accent); }
.task-form .field { margin-bottom: 1rem; }
.task-form textarea { width: 100%; resize: vertical; font: inherit; }
/* ---------- Editor con formato del cuerpo de la tarea (28-jul-2026) ----------
   Caja única con la barra arriba y el área de escritura abajo, para que se lea como un solo campo
   (antes era un textarea). El alto es el mismo que tenía el textarea de 12 filas. */
.rt { border: 1px solid var(--line); border-radius: 12px; background: var(--paper); overflow: hidden; }
.rt:focus-within { border-color: var(--ink-soft); }
.rt-bar { display: flex; align-items: center; gap: .15rem; flex-wrap: wrap; padding: .35rem .45rem; background: var(--bg); border-bottom: 1px solid var(--line-2); }
.rt-b { width: 30px; height: 30px; border: 1px solid transparent; border-radius: 8px; background: none; color: var(--ink-soft); cursor: pointer; display: grid; place-items: center; }
.rt-b:hover { background: var(--paper); border-color: var(--line-2); color: var(--ink); }
.rt-b.is-on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-deep); }
.rt-b .app-icon { width: 16px; height: 16px; }
.rt-sep { width: 1px; height: 18px; background: var(--line-2); margin: 0 .3rem; }
.rt-area { min-height: 230px; max-height: 46vh; overflow-y: auto; padding: .75rem .85rem; font: inherit; font-size: .88rem; line-height: 1.55; color: var(--ink); outline: none; }
.rt-area:empty::before { content: attr(data-ph); color: var(--ink-mute); }
.rt-area p { margin: 0 0 .6rem; }
.rt-area p:last-child { margin-bottom: 0; }
.rt-area p.task-h { font-weight: 600; }
.rt-area ul, .rt-area ol { margin: .2rem 0 .7rem; padding-left: 1.4rem; }
.rt-area li { margin: .15rem 0; }
.rt-area img { max-width: 100%; height: auto; border-radius: 10px; display: block; margin: .5rem 0; }
.rt-area a { color: var(--accent-deep); text-decoration: underline; }
/* Documento insertado dentro del texto: en el editor se ve como una ficha compacta que no se puede
   escribir por dentro; al pintar la tarea se convierte en la tarjeta de siempre (.task-doc). */
.rt-doc { display: inline-flex; align-items: center; gap: .35rem; vertical-align: middle; max-width: 100%;
  background: var(--bg); border: 1px solid var(--line-2); border-radius: 8px; padding: .25rem .5rem; font-size: .8rem; }
.rt-doc .app-icon { width: 14px; height: 14px; color: var(--accent); flex: 0 0 auto; }
.rt-doc b { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.task-box { background: var(--bg); border: 1px solid var(--line-2); border-radius: 12px; padding: .8rem .9rem; }
.task-box > label { display: flex; align-items: center; gap: .35rem; }
.task-box .app-icon { width: 15px; height: 15px; color: var(--accent); }
.field-hint { display: block; font-size: .72rem; color: var(--ink-mute); margin-top: .4rem; }
.task-form-files { display: flex; flex-direction: column; gap: .4rem; margin-top: .6rem; }
.task-form-file { display: flex; align-items: center; gap: .4rem; font-size: .76rem; background: var(--paper); border: 1px solid var(--line-2); border-radius: 8px; padding: .3rem .5rem; }
.task-form-file .app-icon { width: 14px; height: 14px; }
.task-form-file .tff-img { width: 22px; height: 22px; border-radius: 5px; background-size: cover; background-position: center; flex: 0 0 auto; }
.task-form-file button { margin-left: auto; background: none; border: none; cursor: pointer; transform: rotate(45deg); color: var(--ink-mute); display: grid; place-items: center; }
.task-form-file button .app-icon { width: 14px; height: 14px; }
.task-type-opts { display: flex; flex-direction: column; gap: .5rem; }
/* Solo el borde coral al pasar el cursor; sin animación de fondo/tamaño (pedido del cliente). */
.task-type-opt { display: flex; align-items: flex-start; gap: .6rem; border: 1px solid var(--line-2); border-radius: 10px; padding: .7rem .8rem; cursor: pointer; transition: border-color .15s; }
.task-type-opt:hover { border-color: var(--accent); }
/* El radio debe quedar como CÍRCULO nativo. `.field input` lo estiraba a ancho completo (601px) con
   fondo, borde y transición de "campo de texto" → eso cambiaba tamaños y hacía parpadear el texto al
   cambiar de opción. Se revierte a su tamaño natural, sin animación: el círculo indica la selección. */
.task-type-opt input[type="radio"] { width: auto; flex: 0 0 auto; margin-top: .2rem; padding: 0; background: none; border: 0; border-radius: 0; box-shadow: none; transition: none; }
.task-type-opt input[type="radio"]:focus { outline: none; background: none; }
.task-type-opt b { display: flex; align-items: center; gap: .35rem; font-size: .85rem; font-weight: 500; }
.task-type-opt b .app-icon { width: 15px; height: 15px; color: var(--accent); }
.task-type-opt small { display: block; font-size: .75rem; color: var(--ink-mute); margin-top: .15rem; }

/* ===== Adjuntos en la tarea (vista) ===== */
.task-attach { display: flex; flex-direction: column; gap: .8rem; margin: .9rem 0; }
.task-attach-block { background: var(--bg); border: 1px solid var(--line-2); border-radius: 10px; padding: .7rem .8rem; }
.task-attach-h { display: flex; align-items: center; gap: .35rem; font-size: .74rem; font-weight: 600; color: var(--ink-soft); margin-bottom: .5rem; }
.task-attach-h .app-icon { width: 14px; height: 14px; color: var(--accent); }
.task-link { display: block; font-size: .82rem; color: var(--accent-deep); overflow-wrap: anywhere; padding: .15rem 0; } /* excepción: enlaces */
.task-link:hover { text-decoration: underline; }
/* Documentos con vista previa embebida + descarga */
.task-docs { display: flex; flex-direction: column; gap: .55rem; }
.task-doc { background: var(--paper); border: 1px solid var(--line-2); border-radius: 10px; overflow: hidden; }
.task-doc-row { display: flex; align-items: center; gap: .6rem; padding: .55rem .65rem; flex-wrap: wrap; }
.task-doc-ico { width: 30px; height: 30px; flex: 0 0 auto; border-radius: 8px; display: grid; place-items: center; background: var(--accent-soft); color: var(--accent-deep); }
.task-doc-ico .app-icon { width: 16px; height: 16px; }
.task-doc-name { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; flex: 1 1 auto; }
.task-doc-name b { font-size: .84rem; color: var(--ink); font-weight: 600; }
.task-doc-name small { font-size: .7rem; color: var(--ink-mute); }
.task-doc-actions { display: flex; align-items: center; gap: .35rem; flex: 0 0 auto; flex-wrap: wrap; }
.task-doc-btn { display: inline-flex; align-items: center; gap: .3rem; font-size: .76rem; font-weight: 500; border: 1px solid var(--line); border-radius: 8px; padding: .35rem .55rem; background: var(--paper); color: var(--ink-soft); cursor: pointer; white-space: nowrap; transition: background .15s, color .15s, border-color .15s; }
.task-doc-btn .app-icon { width: 14px; height: 14px; }
.task-doc-btn:hover { background: var(--accent-soft); color: var(--accent-deep); border-color: var(--accent); }
.task-doc-btn.is-open { background: var(--accent); color: #fff; border-color: var(--accent); }
.task-doc-btn.ghost { color: var(--ink-mute); }
.task-doc-frame { border-top: 1px solid var(--line-2); background: var(--ink); }
.task-doc-frame iframe { display: block; width: 100%; height: 460px; border: 0; }
.lesson-check.readonly { cursor: default; }
iframe.task-video-el { width: 100%; height: 100%; border: 0; }

/* ===== Seguimiento Clientes (antes "Panel de coach") ===== */
.coach-dash { margin-bottom: 1.6rem; padding: 1.5rem 1.6rem; }
.coach-dash-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.1rem; }
/* Título en la misma tipografía (Inter) que el subtexto, no serif */
.coach-dash-head h2, .coach-dash-h2 { font-family: var(--sans); font-weight: 600; font-size: 1rem; letter-spacing: 0; color: var(--ink); }
.coach-dash-head p { font-size: .82rem; color: var(--ink-mute); margin: .25rem 0 0; }
.seg-filters { display: flex; gap: .6rem; flex-wrap: wrap; }
.seg-filter { display: flex; flex-direction: column; gap: .25rem; font-size: .68rem; color: var(--ink-mute); font-weight: 600; }
/* Barra de herramientas de la tabla de Seguimiento (4-ago-2026): buscador + filtro por consultor,
   juntos a la derecha del título. En pantallas chicas bajan en fila propia y el buscador se estira. */
.seg-tools { display: flex; align-items: flex-end; gap: .8rem; flex-wrap: wrap; }
.seg-search input { border: 1px solid var(--line); border-radius: .55rem; padding: .42rem .7rem; font: inherit; font-size: .85rem; min-width: 190px; background: var(--paper, #fff); color: inherit; }
.seg-search input:focus { outline: none; border-color: var(--accent); }
@media (max-width: 560px) { .seg-tools { width: 100%; } .seg-search { flex: 1; } .seg-search input { width: 100%; min-width: 0; } }

/* Cuerpo de publicación con formato (4-ago-2026): mismo render que el cuerpo de una tarea
   (.task-rich pone viñetas y numeración); acá solo se ajusta al tono del feed. */
.fp-body-rich { font-size: .95rem; color: var(--ink-soft); line-height: 1.55; text-align: justify; }
.fp-body-rich p { margin: 0 0 .55rem; }
.fp-body-rich p:last-child { margin-bottom: 0; }
.fp-body-rich ul, .fp-body-rich ol { margin: 0 0 .55rem 1.3rem; }

/* Menciones con @ (4-ago-2026): el nombre resaltado dentro del texto, y el desplegable del
   compositor/comentarios. El panel cuelga de <body> con fixed — regla de los menús flotantes. */
.mention { color: var(--accent-deep); font-weight: 600; cursor: pointer; }
.mention:hover { text-decoration: underline; }
.mention-menu { position: fixed; z-index: 3200; background: var(--paper, #fff); border: 1px solid var(--line); border-radius: .7rem; box-shadow: 0 10px 30px rgba(0,0,0,.14); padding: .25rem; overflow: hidden; }
.mention-opt { display: flex; align-items: center; gap: .6rem; width: 100%; border: 0; background: none; font: inherit; font-size: .86rem; padding: .4rem .6rem; border-radius: .5rem; cursor: pointer; text-align: left; color: inherit; min-width: 0; }
.mention-opt.is-active, .mention-opt:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
/* La foto va sobre `.side-avatar`, que ya da el círculo y el respaldo con la inicial: aquí solo el
   tamaño (9-ago-2026, opción A del cliente). */
.mention-av { width: 2.25rem; height: 2.25rem; font-size: .82rem; flex: none; }
.mention-txt { min-width: 0; display: block; line-height: 1.25; }
.mention-txt b { display: block; font-weight: 600; font-size: .87rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mention-txt small { display: block; font-size: .74rem; color: var(--ink-mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Lo que llevas escrito, resaltado — sin fondo amarillo del navegador. */
.mention-txt mark { background: none; color: var(--accent-deep); font-weight: 700; }

/* Tarjeta del evento vinculado a una publicación (4-ago-2026). */
.fp-event { display: flex; align-items: center; gap: .7rem; border: 1px solid var(--line); border-radius: .7rem; padding: .6rem .8rem; margin: .6rem 0 .2rem; background: color-mix(in srgb, var(--accent) 5%, transparent); }
.fp-event-ico { display: grid; place-items: center; width: 2.1rem; height: 2.1rem; border-radius: .55rem; background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent-deep); flex: none; }
.fp-event-info { display: flex; flex-direction: column; gap: .1rem; min-width: 0; flex: 1; }
.fp-event-info b { font-size: .9rem; line-height: 1.25; }
.fp-event-info span { font-size: .76rem; color: var(--ink-mute); }
.fp-event .btn { flex: none; }
@media (max-width: 560px) { .fp-event { flex-wrap: wrap; } .fp-event .btn { width: 100%; } }
.seg-filter select { font: inherit; font-size: .82rem; font-weight: 400; text-transform: none; letter-spacing: 0; padding: .4rem 1.8rem .4rem .7rem; border: 1px solid var(--line); border-radius: 100px; background: var(--paper); color: var(--ink); cursor: pointer; appearance: none; -webkit-appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237a736b' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right .6rem center; background-size: 12px; }
/* Scroll horizontal en móvil (mismo patrón que .mstu-scroll/.mstu-table): en vez de comprimir las
   columnas hasta volverlas ilegibles, la tabla mantiene un ancho mínimo y el wrapper scrollea. */
.metric-table-scroll, .track-table-scroll, .coachm-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Tabla de métricas (bordes contenidos con margen y esquinas redondeadas) */
.metric-table { border: 1px solid var(--line-2); border-radius: 12px; overflow: hidden; min-width: 560px; }
.metric-row { display: grid; grid-template-columns: minmax(150px, 1.6fr) 1fr 46px 18px; align-items: center; gap: 1rem; padding: .72rem var(--card-pad); border-bottom: 1px solid var(--line-2); cursor: pointer; transition: background .15s; }
.metric-row:hover { background: var(--bg); }
.metric-row.is-open { background: var(--bg); }
.metric-name { font-size: .84rem; font-weight: 450; color: var(--ink); line-height: 1.3; }
.metric-bar { height: 8px; border-radius: 100px; background: var(--bg-3); overflow: hidden; }
.metric-bar i { display: block; height: 100%; border-radius: 100px; background: var(--grad-portada); }
/* ===== Bitácora · piezas nuevas ===== */
/* Tarjeta de datos CLARA (Activación, Finalización, Embudo, Mezcla). Distinta de .metrics-chart-card,
   que es OSCURA (--grad-portada) y reservada para el hero y los gráficos SVG diseñados sobre negro.
   Aquí el subtítulo .metrics-chart-sub —pensado en claro para tarjeta oscura— se reescribe a tinta. */
/* Ritmo vertical de la página (1.4rem entre bloques): el hero y la tarjeta de recorrido son cards
   SUELTAS (no van dentro de un grid que ya da el gap), así que necesitan su propio margen inferior. */
.bit-hero { margin-bottom: 1.4rem; }
.bit-chart-full { margin-bottom: 1.4rem; }
/* Listas accionables (estancados, sin arrancar, sin coach, sin plan): con ~300 alumnos pueden tener
   muchas filas. La caja mantiene un alto máximo y hace scroll interno, sin desbordar la página. */
.flag-scroll { max-height: 340px; overflow-y: auto; margin: 0 -.2rem; padding: 0 .2rem; }
.flag-scroll::-webkit-scrollbar { width: 5px; }
.flag-scroll::-webkit-scrollbar-thumb { background: rgba(28,26,24,.16); border-radius: 4px; }
/* Las bit-panel son .app-card, que NO trae padding (cada tarjeta lo pone explícito con --card-pad).
   Sin esto el título y el subtítulo tocaban el borde, mientras el contenido de abajo (número, embudo,
   barras) traía su propio --card-pad y quedaba más adentro. Se le da el mismo padding que las demás
   tarjetas de la página y se quita el margen horizontal DOBLE de los hijos que ya lo traían. */
.bit-panel { display: flex; flex-direction: column; padding: 1.3rem var(--card-pad) 1.4rem; }
.bit-panel .bit-big { padding-left: 0; padding-right: 0; }
.bit-panel .bit-funnel { padding-left: 0; padding-right: 0; }
.bit-panel .metric-table { min-width: 0; }  /* en media tarjeta la tabla no necesita el mínimo de 560px */
.bit-panel h3 { font-size: 1.02rem; font-weight: 600; color: var(--ink); }
/* El subtítulo .metrics-chart-sub está pensado en color CLARO para las tarjetas oscuras; en las
   tarjetas CLARAS (bit-panel y flag-cards) se reescribe a tinta o quedaría ilegible sobre el crema. */
.bit-panel .metrics-chart-sub, .metrics-flag-card .metrics-chart-sub { color: var(--ink-soft); min-height: 0; margin: .2rem 0 .8rem; }
/* Hero de 5 tiles a lo ancho (en la tarjeta oscura de cabecera). */
.bit-hero .mhero-stats { grid-template-columns: repeat(5, minmax(0, 1fr)); }
@media (max-width: 880px) { .bit-hero .mhero-stats { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 560px) { .bit-hero .mhero-stats { grid-template-columns: repeat(2, 1fr); } }
/* Fila de barra SIN desplegable (finalización por plan, mezcla): 3 columnas, no 4 — sin cursor. */
.metric-row.is-static { grid-template-columns: minmax(120px, 1.6fr) 1fr 52px; cursor: default; }
.metric-row.is-static:hover { background: none; }
.metric-name .hint { color: var(--ink-mute); font-weight: 400; font-size: .74rem; }
.metric-val { font-variant-numeric: tabular-nums; }
/* Número grande de una métrica destacada (activación). */
.bit-big { display: flex; align-items: baseline; gap: .7rem; padding: 1rem var(--card-pad) 1.2rem; }
.bit-big-n { font-size: 2.6rem; font-weight: 600; line-height: 1; color: var(--accent-deep); }
.bit-big-l { font-size: .86rem; color: var(--ink-soft); }
/* Embudo de 3 pasos (upsell). */
.bit-funnel { display: flex; align-items: stretch; gap: .5rem; padding: 1rem var(--card-pad); }
.bit-funnel-step { flex: 1 1 0; min-width: 0; text-align: center; background: var(--bg); border: 1px solid var(--line-2); border-radius: 12px; padding: .9rem .5rem; }
.bit-funnel-step b { display: block; font-size: 1.7rem; font-weight: 600; line-height: 1; color: var(--ink); }
.bit-funnel-step span { display: block; margin-top: .3rem; font-size: .72rem; color: var(--ink-mute); }
.bit-funnel-step.is-win { background: var(--accent-soft); border-color: rgba(242,107,94,.3); }
.bit-funnel-step.is-win b { color: var(--accent-deep); }
.bit-funnel-arrow { width: 16px; height: 16px; flex: 0 0 auto; align-self: center; color: var(--ink-mute); }
/* Rojo de riesgo en tiles de la cabecera y en la tabla por coach. */
.mhero-stat.is-risk .n { color: #ff9d93; }
.coachm-stat b.is-risk { color: #E5484D; }
@media (max-width: 560px) { .bit-funnel { flex-direction: column; } .bit-funnel-arrow { transform: rotate(90deg); } }
.metric-val { font-size: .82rem; font-weight: 600; color: var(--ink); text-align: right; font-variant-numeric: tabular-nums; }
.metric-chevron { width: 16px; height: 16px; color: var(--ink-mute); transition: transform .25s; justify-self: end; }
.metric-row.is-open .metric-chevron { transform: rotate(180deg); }
/* Desplegable: alumnos que más se demoran en la etapa */
/* SIN animación de entrada (11-ago-2026, pedido del cliente). Cada acción del desplegable —marcar
   una sesión, marcar el onboarding, corregir su fecha— repinta Seguimiento DOS veces: una por el
   clic y otra por el eco en vivo de la base. Con `viewIn` puesta, el panel entero se reanimaba en
   cada repintado y se veía como un parpadeo doble. El doble repintado se conserva a propósito: es lo
   que mantiene la pantalla al día entre usuarios sin recargar. */
.metric-students { border-bottom: 1px solid var(--line-2); background: var(--bg); padding: .2rem var(--card-pad) 1rem; }
.metric-row:last-child { border-bottom: none; }
.metric-row.is-open:last-child { border-bottom: 1px solid var(--line-2); }
.metric-students:last-child { border-bottom: none; }
.mstu-empty { font-size: .82rem; color: var(--ink-mute); padding: .9rem .2rem .3rem; margin: 0; }
.mstu-scroll { overflow-x: auto; padding-top: .4rem; }
.mstu-table { min-width: 900px; display: flex; flex-direction: column; }
.mstu-row { display: grid; grid-template-columns: 1.7fr .9fr 1.1fr 1.1fr .9fr .9fr .85fr 1.5fr; gap: .7rem; align-items: center; padding: .55rem 0; border-top: 1px solid var(--line-2); font-size: .8rem; color: var(--ink-soft); }
.mstu-head { border-top: none; font-size: .66rem; color: var(--ink-mute); font-weight: 600; }
.mstu-alumno { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.mstu-alumno b { font-weight: 500; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mstu-av { width: 30px; height: 30px; flex: 0 0 auto; font-size: .72rem; }
.mstu-plan { color: var(--ink-mute); }
.mstu-pct { font-weight: 600; color: var(--ink); }
.mstu-row small { color: var(--ink-mute); margin-left: .35rem; }
.mstu-days { color: var(--accent-deep); font-variant-numeric: tabular-nums; }
.mstu-act { font-size: .7rem; font-weight: 600; padding: .2rem .55rem; border-radius: 100px; display: inline-block; line-height: 1.35; }
.mstu-act.warn { background: #fbe4e0; color: #b3402f; }
.mstu-act.up { background: #f7ecd6; color: #8a5a00; }
.mstu-act.ok { background: var(--bg-2); color: var(--ink-soft); }
.coach-dash-note { font-size: .74rem; color: var(--ink-mute); margin: 1rem 0 0; }

/* Tabla de seguimiento de alumnos (mismos márgenes que "Métricas de avance": card con padding + tabla con borde/radio contenidos) */
.track-card { padding: 1.5rem 1.6rem; margin-bottom: 1.6rem; }
.track-title { font-family: var(--serif); font-weight: 400; font-size: 1.15rem; letter-spacing: -.01em; display: flex; align-items: center; gap: .55rem; margin: 0 0 1rem; }
.track-head-row { display: flex; align-items: center; justify-content: space-between; gap: .8rem 1.2rem; flex-wrap: wrap; margin-bottom: 1rem; }
/* El buscador a plomo con la columna «Alumno» (11-ago-2026, pedido del cliente). El sangrado NO es
   un número a ojo: es el mismo `--card-pad` que sangra las celdas de la tabla, más el 1px del borde
   del recuadro. Si mañana cambia el relleno de la tabla, el buscador se mueve con ella. */
.track-search-row { padding-left: calc(var(--card-pad) + 1px); margin-bottom: .7rem; }
/* Fuera de la fila de filtros el campo se estiraba a todo el ancho de la tarjeta y parecía otra
   cosa. Se acota, y en pantallas estrechas cae al 100% él solo. */
/* Cápsula redondeada como la de la barra superior (`.topbar-search`): mismo radio y mismo relleno,
   pero sobre fondo claro. Y algo más larga que antes —de 320 a 400— porque se busca por nombre y
   apellido y a 320 no cabían. */
.track-search-row .seg-search input { width: min(400px, 100%); border-radius: 100px; padding: .62rem 1.15rem; font-size: .88rem; background: var(--bg); }
.track-search-row .seg-search input:focus { background: var(--paper); border-color: var(--accent); }
.track-head-row .track-title { margin: 0; }
.track-table { display: flex; flex-direction: column; border: 1px solid var(--line-2); border-radius: 12px; overflow: hidden; min-width: 680px; }
.track-row { display: grid; grid-template-columns: 1.6fr 1fr 1.2fr 1fr .9fr; align-items: center; gap: .8rem; padding: .72rem var(--card-pad); border-top: 1px solid var(--line-2); cursor: pointer; transition: background .15s; }
.track-row:hover:not(.track-head) { background: var(--bg); }
.track-head { cursor: default; border-top: none; font-size: .7rem; color: var(--ink-mute); font-weight: 600; }
.track-head:hover { background: none; }
.track-cell { min-width: 0; font-size: .86rem; }
.track-alumno { display: flex; align-items: center; gap: .6rem; }
.track-alumno b { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.track-av { width: 34px; height: 34px; flex: 0 0 auto; font-size: .8rem; }
.track-plan { color: var(--ink-soft); }
.track-avance { display: flex; align-items: center; gap: .5rem; }
.track-bar { flex: 1; height: 8px; border-radius: 100px; background: var(--bg-3); overflow: hidden; min-width: 50px; }
.track-bar i { display: block; height: 100%; border-radius: 100px; background: var(--grad-coral); }
.track-avance span { font-size: .8rem; font-weight: 600; flex: 0 0 auto; }
.track-conn { color: var(--ink-mute); }
.track-conn .lc-recent { color: #1f7a4d; font-weight: 600; }
.track-conn .lc-stale { color: var(--ink-soft); }
/* "Activo" es un ESTADO, no una fecha: lleva punto verde para que se distinga de un vistazo de
   "Hace 3 minutos", que es lo más parecido que puede salir justo al lado. El selector no lleva
   `.track-conn` delante porque la etiqueta sale también en el detalle del alumno. */
.lc-live { color: #1f7a4d; font-weight: 600; display: inline-flex; align-items: center; gap: .35rem; }
.lc-live::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: #1f7a4d;
                   box-shadow: 0 0 0 3px rgba(31, 122, 77, .16); flex: 0 0 auto; }
.track-estado { display: flex; align-items: center; justify-content: space-between; gap: .4rem; }
.estado-badge { font-size: .72rem; font-weight: 600; padding: .22rem .6rem; border-radius: 100px; }
.estado-badge.activo { background: #e2f4ea; color: #1f7a4d; }
.estado-badge.riesgo { background: #fbe4e0; color: #b3402f; }
.estado-badge.nuevo { background: #f7ecd6; color: #8a5a00; }
/* Estado v2 por atraso (27-jul-2026): al día / atención / crítico / en pausa / finalizado. */
.estado-badge.aldia { background: #e2f4ea; color: #1f7a4d; }
.estado-badge.atencion { background: #f7ecd6; color: #8a5a00; }
.estado-badge.critico { background: #f3d0c9; color: #7c2417; }
.estado-badge.pausa { background: #ece7dd; color: #6b6357; }
.estado-badge.fin { background: #e6eaf5; color: #33508f; }
.estado-wrap { display: inline-flex; flex-direction: column; gap: 2px; align-items: flex-start; min-width: 0; }
.estado-sub { font-size: .66rem; font-weight: 600; color: #b3402f; white-space: nowrap; }
.track-chevron { width: 16px; height: 16px; color: var(--ink-mute); transition: transform .25s; flex: 0 0 auto; }
.track-row.is-open .track-chevron { transform: rotate(180deg); }
.track-detail { padding: .2rem var(--card-pad) 1rem; border-top: 1px dashed var(--line-2); }   /* sin viewIn: ver la nota de .metric-students */
/* Lo que se MARCA va primero y con más espacio (1-ago-2026, pedido del cliente): onboarding y
   sesiones a la izquierda —es lo único con lo que el consultor interactúa a diario— y las fechas a
   la derecha, en un recuadro de consulta. En el HTML el orden de los bloques ya es ese; acá solo se
   reparten los anchos. */
.track-detail-grid { display: grid; grid-template-columns: 1.35fr .85fr; gap: 1.6rem; padding-top: 1rem; }
.track-metrics, .track-sessions { min-width: 0; }
/* Las fechas, como ficha de consulta: fondo propio para que se lean como "dato", no como acción. */
.track-metrics { background: var(--bg); border-radius: 14px; padding: .9rem 1rem; align-self: start; }
.track-metrics .tm-row { border-bottom-color: rgba(28, 26, 24, .08); }
.track-metrics .tm-row:last-child { border-bottom: none; padding-bottom: 0; }
.tm-row { display: flex; justify-content: space-between; gap: .8rem; padding: .4rem 0; border-bottom: 1px solid var(--line-2); font-size: .84rem; }
.tm-row span { color: var(--ink-mute); }
.tm-row b { font-weight: 500; text-align: right; }
/* Lápiz para corregir la fecha de onboarding, dentro de la propia fila. Va pegado al dato y no
   como botón aparte: es una corrección puntual, no una acción de la ficha. */
.tm-edit { border: 0; background: none; padding: 0 0 0 .3rem; cursor: pointer; color: var(--ink-mute);
           line-height: 0; vertical-align: -2px; transition: color .18s ease; }
.tm-edit:hover { color: var(--accent-deep); }
.tm-edit .app-icon { width: 13px; height: 13px; }
.verdict.activo { color: #1f7a4d; }
.verdict.riesgo { color: #b3402f; }
.sess-mark-row { display: flex; flex-direction: column; gap: .5rem; padding: .7rem 0; border-bottom: 1px solid var(--line-2); }
.sess-mark-info { display: flex; justify-content: space-between; align-items: baseline; gap: .6rem; }
.sess-mark-row .sess-name { font-size: .84rem; color: var(--ink-soft); }
.sess-mark-tally { font-size: .8rem; color: var(--ink-mute); flex: 0 0 auto; }
.sess-mark-tally b { color: var(--ink); font-size: .92rem; }
/* Círculos numerados: gris con número crema; coral al marcarlos */
.sess-dots { display: flex; flex-wrap: wrap; gap: .42rem; }
.sess-dot { width: 30px; height: 30px; border-radius: 50%; border: none; background: var(--ink-mute); color: var(--paper); font: 600 .78rem/1 var(--sans); cursor: pointer; display: grid; place-items: center; transition: background .15s, transform .1s; }
.sess-dot:hover { transform: scale(1.08); }
.sess-dot.is-on { background: var(--accent); color: #fff; }
/* Marcador de Onboarding: check (realizada) / cruz (no realizada) */
.onb-mark-row { display: flex; align-items: center; justify-content: space-between; gap: .6rem; padding: .55rem 0 .2rem; }
/* El onboarding dejó de tener título propio: es UNA pregunta de sí o no, así que va como una línea
   dentro de su recuadro, encima de las sesiones. Se le da fondo para que no se lea como parte de la
   lista de sesiones que viene debajo. */
.onb-mark-row--solo { background: var(--bg); border-radius: 12px; padding: .55rem .8rem; margin-bottom: 1rem; }
.onb-mark-q { font-size: .84rem; color: var(--ink-soft); }
.onb-mark-btns { display: inline-flex; gap: .35rem; flex: 0 0 auto; }
.onb-mark { width: 36px; height: 30px; border-radius: 9px; border: 1px solid var(--line-2); background: var(--paper); color: var(--ink-mute); font-size: .95rem; font-weight: 700; cursor: pointer; display: grid; place-items: center; transition: background .12s, border-color .12s, color .12s; }
.onb-mark:hover { border-color: var(--ink-soft); }
.onb-mark--yes.is-on { background: #e2f4ea; border-color: #1f7a4d; color: #1f7a4d; }
.onb-mark--no.is-on { background: var(--accent-soft); border-color: var(--accent-deep); color: var(--accent-deep); }
/* Solo lectura: cuando el usuario no es el coach asignado del alumno (no puede editar registros) */
.sess-dot.is-readonly, .onb-mark.is-readonly { cursor: default; }
.sess-dot.is-readonly:hover { transform: none; }
.onb-mark.is-readonly:hover { border-color: var(--line-2); }
.onb-mark.is-readonly:not(.is-on) { opacity: .5; }
.track-readonly-note { margin-top: .7rem; font-size: .72rem; color: var(--ink-mute); font-style: italic; }
.track-detail-actions { margin-top: 1rem; display: flex; gap: .6rem; flex-wrap: wrap; }
.track-detail-actions .app-icon { width: 15px; height: 15px; }
/* En el teléfono las dos columnas se apilan. No existía y las dos se aplastaban a 375px: los
   círculos numerados salían en cuatro filas dentro de media pantalla. Va DESPUÉS de la regla base
   por la trampa de cascada de siempre. */
@media (max-width: 720px) {
  .track-detail-grid { grid-template-columns: minmax(0, 1fr); gap: 1.2rem; }
}

/* Tarjeta de sesiones en el perfil (negro/coral degradado) */
.sessions-card { padding: 0 !important; overflow: hidden; }
.sessions-head { background: var(--grad-portada); color: var(--paper); padding: .9rem 1.1rem; display: flex; justify-content: space-between; align-items: baseline; }
.sessions-head-label { font-size: .82rem; font-weight: 500; letter-spacing: .01em; }
.sessions-head-count { font-family: var(--serif); font-size: 1.5rem; line-height: 1; }
.sessions-head-count small { font-size: .9rem; opacity: .8; }
.sessions-list { padding: .8rem var(--card-pad) 1rem; display: flex; flex-direction: column; gap: .7rem; }
.sess-row { display: grid; grid-template-columns: 1fr auto; gap: .15rem .6rem; align-items: center; }
.sess-row .sess-name { font-size: .82rem; color: var(--ink-soft); }
.sess-row .sess-count { font-size: .8rem; font-weight: 600; color: var(--ink); }
.sess-bar { grid-column: 1 / -1; height: 6px; border-radius: 100px; background: var(--bg-3); overflow: hidden; }
.sess-bar i { display: block; height: 100%; border-radius: 100px; background: var(--grad-coral); }
.seg-count { font-family: var(--sans); font-size: .72rem; font-weight: 600; color: var(--accent-deep); background: var(--bg-2, #efe7e0); border-radius: 100px; padding: .12rem .5rem; }
.seg-back { margin-bottom: 1rem; }
.seg-student-hero { display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem; padding: var(--card-pad); }
.seg-student-av-lg { width: 58px; height: 58px; flex: 0 0 auto; font-size: 1.1rem; }
.seg-student-hero-id { flex: 1; min-width: 0; }
/* Nombre del alumno en el hero de Seguimiento: misma tipografía que el subtexto ("Alumno · Plan
   Premium"), que hereda var(--sans) — antes usaba var(--serif) (Raleway). En negrita para
   distinguirlo (pedido del cliente, 13-jul-2026). */
.seg-student-hero-id h2 { font-family: var(--sans); font-weight: 700; font-size: 1.45rem; letter-spacing: -.01em; }
.seg-student-hero-id span { font-size: .8rem; color: var(--ink-mute); }
.seg-student-note { display: flex; align-items: center; gap: .4rem; font-size: .82rem; color: var(--ink-soft); background: #fbeee9; border: 1px solid #f3d6cc; border-radius: 10px; padding: .55rem .8rem; margin-bottom: 1.2rem; }
.seg-student-note .app-icon { width: 15px; height: 15px; color: var(--accent); flex: 0 0 auto; }
.seg-plan-block + .seg-plan-block { margin-top: 1.4rem; }

/* ===== Conversación finalizada (subtexto centrado) ===== */
/* La barra bloqueada de una conversación finalizada (11-ago-2026, pedido del cliente). Se ve igual
   que la de escribir —misma altura, mismo sitio— pero apagada: así se entiende de un vistazo que ahí
   es donde se escribiría y que ahora no se puede. `cursor: not-allowed` en toda la barra para que el
   ratón lo diga también antes de pulsar. */
.conv-form--locked { opacity: .72; cursor: not-allowed; }
.conv-form--locked textarea { background: var(--bg-2); color: var(--ink-mute); resize: none; cursor: not-allowed; }
.conv-form--locked textarea::placeholder { color: var(--ink-mute); }
.conv-form--locked .btn { pointer-events: none; }
/* El candado ocupa el sitio del botón del micrófono, para que la barra no cambie de forma. */
.conv-tool.is-locked { display: grid; place-items: center; color: var(--ink-mute); background: var(--bg-2); cursor: not-allowed; }
.conv-ended-note { display: flex; align-items: center; justify-content: center; gap: .4rem; text-align: center; font-size: .8rem; color: var(--ink-mute); margin: 1.1rem auto .3rem; padding: .3rem .9rem; }
.conv-ended-note .app-icon { width: 14px; height: 14px; }

/* ===== Nav badge (Mensajes) ===== */
/* El menú lateral, cuando SOLO hay conversaciones marcadas a mano: punto coral sin número, igual
   que en el listado de chats. Con número no diría nada — esa marca la pusiste tú. */
.nav-badge--dot { width: 10px; height: 10px; min-width: 0; padding: 0; border-radius: 50%; font-size: 0; line-height: 0; }
.nav-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 4px; border-radius: 100px; background: var(--accent); color: var(--paper); font-size: .64rem; font-weight: 700; line-height: 1; margin-left: auto; }
.side-link .nav-badge { flex-shrink: 0; }

/* ===== Burbuja de acceso rápido a Mensajes ===== */
.msg-fab { position: fixed; right: 5.4rem; bottom: 1.4rem; width: 52px; height: 52px; border-radius: 50%; border: none; padding: 0; cursor: pointer; z-index: 60;
  background: var(--ink); color: var(--paper); box-shadow: 0 8px 22px rgba(28,26,24,.28); display: grid; place-items: center; transition: transform .18s var(--ease-out), box-shadow .18s; }
.msg-fab:hover { transform: translateY(-2px) scale(1.04); box-shadow: 0 12px 30px rgba(28,26,24,.38); }
.msg-fab.is-open { transform: scale(.92); }
.msg-fab-ico { width: 22px; height: 22px; }
/* Badge de no leídos en la burbuja — idéntico al de la campana (.icon-btn .dot) */
.msg-fab-badge { position: absolute; top: -4px; right: -4px; min-width: 19px; height: 19px; padding: 0 5px; border-radius: 100px;
  background: var(--accent); color: #fff; border: 2px solid var(--paper);
  font-size: .68rem; font-weight: 700; line-height: 1; display: flex; align-items: center; justify-content: center;
  box-shadow: 0 2px 5px rgba(28,26,24,.35); }
.msg-fab-badge[hidden] { display: none; }
/* Panel con gradiente blanco-coral muy suave */
/* top:5.5rem es un piso fijo por debajo del topbar (sticky, ~4.6rem de alto, z-index 70 > 61 del panel):
   evita que el panel crezca por encima del topbar y quede tapado en ventanas no maximizadas, donde
   min(520px, 100vh - 8rem) no dejaba suficiente margen. Con top+bottom fijados, la altura se autoajusta
   al espacio real disponible; max-height conserva el tamaño de tarjeta flotante en pantallas altas. */
/* Anclada ABAJO, no arriba. Antes tenía top + bottom + max-height a la vez: al toparse con el
   máximo, la caja se pegaba al top y dejaba un hueco enorme sobre la burbuja en pantallas altas.
   La protección contra el topbar (sticky, z-index 70) sigue: en vez de `top`, el alto máximo
   descuenta esos 5.5rem, así nunca llega a taparse. NO volver a poner `top` acá. */
/* Alto FIJO, no máximo: con `max-height` la caja se encogía hasta el contenido y con dos
   conversaciones se veía mucho más chica que con diez. El `max(320px, …)` es un suelo para pantallas
   muy bajas, donde el descuento del topbar daría un valor negativo. */
.msg-mini { position: fixed; right: 5.4rem; bottom: 5.35rem; z-index: 61; width: min(360px, calc(100vw - 2rem)); height: min(620px, max(320px, calc(100vh - 10.85rem)));
  background: linear-gradient(180deg, #ffffff 0%, #fff6f5 55%, #fce9e7 100%); border: 1px solid rgba(229,107,94,.22); border-radius: 20px; box-shadow: 0 20px 55px rgba(28,26,24,.22), 0 4px 16px rgba(229,107,94,.1); overflow: hidden; display: flex; flex-direction: column; animation: nubiaPop .22s var(--ease-out); }
/* Encabezado sólido oscuro */
.msg-mini-head { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; padding: .8rem 1rem; gap: .5rem; background: #1C1A18; border-bottom: 1px solid rgba(255,255,255,.1); }
.msg-mini-title { font-size: .9rem; font-weight: 600; color: #f4f1ea; flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.msg-mini .nubia-mini-btn { color: rgba(244,241,234,.6); }
.msg-mini .nubia-mini-btn:hover { background: rgba(255,255,255,.12); color: #f4f1ea; }
/* Área de burbujas */
.msg-mini-body { flex: 1 1 auto; overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: .55rem; padding: 1rem .9rem; }
.msg-mini-body::-webkit-scrollbar { width: 4px; }
.msg-mini-body::-webkit-scrollbar-thumb { background: rgba(229,107,94,.22); border-radius: 4px; }
/* Burbujas estilo WhatsApp — recibidas (izquierda) */
.msg-mini-body .bubble { position: relative; max-width: 80%; padding: .55rem .85rem .42rem; border-radius: 18px 18px 18px 4px; background: #ede9e8; color: var(--ink); align-self: flex-start; margin-left: 10px; font-size: .875rem; line-height: 1.4; }
.msg-mini-body .bubble::before { content: ''; position: absolute; bottom: 0; left: -7px; border: 7px solid transparent; border-right-color: #ede9e8; border-left: 0; border-top: 0; }
.msg-mini-body .bubble time { font-size: .63rem; opacity: .55; display: block; margin-top: .25rem; color: var(--ink); }
/* Burbujas enviadas (derecha) */
.msg-mini-body .bubble.mine { background: var(--accent); color: #fff; align-self: flex-end; border-radius: 18px 18px 4px 18px; margin-right: 10px; margin-left: 0; }
.msg-mini-body .bubble.mine::before { display: none; }
.msg-mini-body .bubble.mine::after { content: ''; position: absolute; bottom: 0; right: -7px; border: 7px solid transparent; border-left-color: var(--accent); border-right: 0; border-top: 0; }
.msg-mini-body .bubble.mine time { color: rgba(255,255,255,.65); }
.msg-mini-body .bubble p { font-size: .875rem; margin: 0; }
/* Área de escritura sólida oscura */
.msg-mini-form { flex: 0 0 auto; display: flex; gap: .5rem; padding: .7rem .9rem; background: #1C1A18; border-top: 1px solid rgba(255,255,255,.1); }
.msg-mini-form .conv-input { flex: 1 1 auto; min-width: 0; background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.15); border-radius: 22px; padding: .5rem .85rem; font-size: .88rem; color: #f4f1ea; font-family: inherit; outline: none; transition: border-color .15s; }
.msg-mini-form .conv-input::placeholder { color: rgba(244,241,234,.38); }
.msg-mini-form .conv-input:focus { border-color: var(--accent); }
.msg-mini-form .btn--coral { padding: .45rem .65rem; flex: 0 0 auto; border-radius: 50%; width: 36px; height: 36px; display: grid; place-items: center; }
.msg-mini-form .btn--coral .app-icon { width: 16px; height: 16px; }
/* Lista de conversaciones (vista "threads") con tema claro */
.msg-mini-thread { display: flex; align-items: center; gap: .75rem; width: 100%; padding: .8rem 1rem; border: none; background: none; cursor: pointer; text-align: left; border-bottom: 1px solid rgba(28,26,24,.08); transition: background .12s; }
.msg-mini-thread:hover { background: rgba(28,26,24,.05); }
.msg-mini-thread.is-unread { background: rgba(242,107,94,.1); }
.msg-mini-av { width: 36px; height: 36px; border-radius: 50%; background: rgba(229,107,94,.15); color: var(--accent); font-weight: 700; font-size: .85rem; display: grid; place-items: center; flex: 0 0 auto; }
.msg-mini-meta { flex: 1 1 auto; min-width: 0; }
.msg-mini-meta strong { display: block; font-size: .88rem; color: var(--ink); }
.msg-mini-meta span { display: block; font-size: .8rem; color: var(--ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.msg-mini-empty { padding: 2rem 1rem; text-align: center; font-size: .88rem; color: var(--ink-soft); }
/* right:5.5rem deja un margen real de ~12px con .nubia-fab (right:1rem, 60px de ancho) en este
   mismo breakpoint — antes quedaba en 5rem y las dos burbujas se superponían ~2-3px (bug real,
   presente en TODOS los anchos porque ambas burbujas se muestran a cualquier usuario logueado). */
@media (max-width: 560px) { .msg-fab { right: 5.5rem; bottom: 1.4rem; } .msg-mini { right: 1rem; left: 1rem; width: auto; bottom: 5.35rem; } }

/* ===== Upsell panel · tabla en coach ===== */
.upsell-table-wrap { border: 1px solid var(--line-2); border-radius: 12px; overflow-x: auto; overflow-y: hidden; }
/* La tabla tiene 5 columnas (Alumno · Plan · Fecha · Estado · Acción); en móvil no caben en el ancho
   de la tarjeta, así que se desplazan con scroll horizontal en vez de recortarse (columna Acción con
   los botones de aprobar/rechazar el upsell quedaba oculta con overflow:hidden). */
.upsell-table { min-width: 520px; }
.upsell-table { width: 100%; border-collapse: collapse; font-size: .86rem; }
.upsell-table th { text-align: left; padding: .72rem var(--card-pad); font-size: .7rem; color: var(--ink-mute); font-weight: 600; }
.upsell-table td { padding: .72rem var(--card-pad); border-top: 1px solid var(--line-2); color: var(--ink); vertical-align: middle; }
.upsell-table tbody tr:hover td { background: var(--bg); }
.upsell-status { display: inline-block; padding: .22rem .6rem; border-radius: 100px; font-size: .72rem; font-weight: 600; }
.upsell-status--pending { background: #fff3d6; color: #8a6000; }
.upsell-status--contacted { background: #e2f4ea; color: #1f7a4d; }
.upsell-status--won { background: var(--accent); color: #fff; }
.upsell-actions { display: inline-flex; gap: .4rem; flex-wrap: wrap; }
/* Bitácora · fila de upsell concretado (opción A: queda visible con el plan habilitado) */
.upsell-won-row td { background: #fff7f5; }
.upsell-won-sub { display: flex; align-items: center; gap: .25rem; font-size: .72rem; font-weight: 500; color: var(--accent-deep); margin-top: .15rem; }
.upsell-won-sub .app-icon { width: 12px; height: 12px; color: var(--accent-deep); flex: 0 0 auto; }

/* ===== Upsell por plan: etiqueta, píldora, banner al habilitar el 2º plan ===== */
/* Etiqueta UPSELL junto al nombre del plan en Seguimiento (paintModules header) */
.plan-upsell-tag { display: inline-flex; align-items: center; gap: .25rem; vertical-align: middle; margin-left: .6rem; font-family: var(--sans); font-size: .62rem; font-weight: 700; letter-spacing: .06em; color: var(--accent-deep); background: var(--accent-soft); border-radius: 100px; padding: .18rem .55rem; }
.plan-upsell-tag .app-icon { width: 12px; height: 12px; color: var(--accent-deep); }
/* Píldora reversible en «Acceso a planes» (Gestionar miembro) */
.plan-row-actions { display: inline-flex; align-items: center; gap: .45rem; flex-wrap: wrap; justify-content: flex-end; }
/* .upsell-pill se retiró el 3-ago-2026 junto con su botón en «Gestionar miembro» (ver app.js).
   No se deja el estilo huérfano: una clase que ya nadie usa acaba resucitando por copiar y pegar. */
/* "Desbloquear [Plan] completo" — desplegable en el bloque de cada plan (Seguimiento) */
.seg-unlock { border: 1px solid var(--line-2); border-top: none; border-radius: 0 0 var(--radius) var(--radius); background: var(--bg); overflow: hidden; margin: -1.4rem 0 1.4rem; }
.seg-unlock-head { display: flex; align-items: center; gap: .6rem; width: 100%; padding: .8rem 1.1rem; cursor: pointer; user-select: none; background: none; border: none; text-align: left; font-family: inherit; }
.seg-unlock-head .ic { width: 30px; height: 30px; flex: 0 0 auto; border-radius: 8px; display: grid; place-items: center; background: var(--accent-soft); color: var(--accent-deep); }
.seg-unlock-head .ic .app-icon { width: 16px; height: 16px; }
.seg-unlock-head .tt { flex: 1; min-width: 0; }
.seg-unlock-head .tt b { display: block; font-size: .88rem; font-weight: 600; }
.seg-unlock-head .tt span { font-size: .73rem; color: var(--ink-mute); }
.seg-unlock-chev { transition: transform .3s var(--ease-out); color: var(--ink-mute); flex: 0 0 auto; }
.seg-unlock.open .seg-unlock-chev { transform: rotate(180deg); }
.seg-unlock-body { padding: 0 1.1rem 1.1rem; display: none; }
.seg-unlock.open .seg-unlock-body { display: block; }
.seg-unlock-callout { background: var(--paper); border: 1px solid var(--line-2); border-radius: 10px; padding: .7rem .8rem; margin-bottom: .7rem; }
.seg-unlock-callout p { font-size: .8rem; color: var(--ink-soft); line-height: 1.55; }
.seg-unlock-go { width: 100%; justify-content: center; }
/* Selector de módulo del segundo desplegable ("Desbloquear un módulo completo"). */
/* Dos desplegables apilados: el margen negativo cancela el inferior del primero para que queden
   pegados como un solo bloque, y el de arriba pierde sus esquinas redondeadas inferiores. */
.seg-unlock + .seg-unlock { margin-top: -1.4rem; }
.seg-unlock:has(+ .seg-unlock) { border-radius: 0; }
/* Banner "¿Es un upsell?" al habilitar el 2º plan núcleo (modal propio) */
.upsell-grant { padding: .2rem 0; }
.ug-head { display: flex; align-items: center; gap: .6rem; margin-bottom: .7rem; }
.ug-ic { width: 38px; height: 38px; flex: 0 0 auto; border-radius: 10px; background: var(--accent); color: #fff; display: grid; place-items: center; position: relative; }
.ug-ic .app-icon { width: 19px; height: 19px; stroke: #fff; color: #fff; }
.ug-ic .ug-dot { position: absolute; top: -3px; right: -3px; width: 12px; height: 12px; border-radius: 50%; background: #e2b400; border: 2px solid var(--paper); }
.ug-head b { font-size: 1rem; font-weight: 700; }
.ug-head .ug-k { display: block; font-size: .66rem; font-weight: 700; letter-spacing: .05em; color: var(--accent-deep); }
.ug-reqline { display: flex; align-items: center; gap: .4rem; font-size: .78rem; color: var(--ink-soft); background: var(--accent-soft); border-radius: 9px; padding: .5rem .7rem; margin-bottom: .7rem; flex-wrap: wrap; }
.ug-reqline .app-icon { width: 14px; height: 14px; color: var(--accent-deep); }
.ug-reqline .ug-st { font-weight: 600; color: #8a6000; background: #fff3d6; border-radius: 100px; padding: .08rem .5rem; font-size: .72rem; }
.ug-body { font-size: .88rem; color: var(--ink-soft); line-height: 1.55; margin-bottom: .5rem; }
.ug-body b, .ug-note b { color: var(--ink); }
.ug-note { font-size: .78rem; color: var(--ink-mute); line-height: 1.5; margin-bottom: 1rem; }
.ug-actions { display: flex; gap: .6rem; flex-wrap: wrap; }
.ug-actions .btn { flex: 1; min-width: 160px; }
.ug-cancel { display: block; width: 100%; margin-top: .7rem; background: none; border: none; color: var(--ink-mute); font-size: .8rem; cursor: pointer; font-family: inherit; padding: .3rem; }
.ug-cancel:hover { color: var(--ink); }

/* ===== Burbuja de acceso rápido a Nubia ===== */
.nubia-fab { position: fixed; right: 1.4rem; bottom: 1.4rem; width: 60px; height: 60px; border-radius: 50%; border: none; padding: 0; cursor: pointer; z-index: 60;
  background: var(--grad-portada, #1C1A18); box-shadow: 0 10px 28px rgba(28,26,24,.32); display: grid; place-items: center; transition: transform .18s var(--ease-out), box-shadow .18s; }
.nubia-fab:hover { transform: translateY(-2px) scale(1.04); box-shadow: 0 14px 34px rgba(28,26,24,.4); }
.nubia-fab.is-open { transform: scale(.92); }
.nubia-fab-av { width: 50px; height: 50px; border-radius: 50%; background-size: cover; background-position: center; border: 2px solid rgba(244,241,234,.85); }
.nubia-fab-dot { position: absolute; top: 4px; right: 4px; width: 13px; height: 13px; border-radius: 50%; background: #1f7a4d; border: 2px solid var(--paper); }
/* Badge de "Nubia sin leer" (1.1.14): respuesta que llegó mientras el alumno no miraba el chat. */
.nubia-fab-badge { position: absolute; top: -4px; right: -4px; min-width: 19px; height: 19px; padding: 0 5px; border-radius: 100px; background: var(--accent); color: #fff; font-size: .68rem; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; border: 2px solid var(--paper); }
/* Mismo criterio que .msg-mini: anclada abajo y con el alto máximo descontando el topbar (sticky,
   z-index 70) en
   ventanas no maximizadas; max-height conserva el tamaño de tarjeta flotante en pantallas altas. */
/* Alto FIJO por el mismo motivo que .msg-mini: con `max-height` la caja se ajustaba al contenido y
   una conversación corta la dejaba mucho más chica. El descuento (11.35rem) y el suelo son los suyos:
   esta burbuja arranca más abajo (bottom 5.85rem) y es más ancha. */
.nubia-mini { position: fixed; right: 1.4rem; bottom: 5.85rem; z-index: 61; width: min(570px, calc(100vw - 2rem)); height: min(640px, max(320px, calc(100vh - 11.35rem)));
  background: var(--paper); border: 1px solid var(--line); border-radius: 18px; box-shadow: 0 18px 50px rgba(28,26,24,.28); overflow: hidden; display: flex; flex-direction: column; animation: nubiaPop .22s var(--ease-out); }
@keyframes nubiaPop { from { opacity: 0; transform: translateY(12px) scale(.98); } to { opacity: 1; transform: none; } }
.nubia-mini-head { flex: 0 0 auto; border-bottom: 1px solid var(--line-2); }
.nubia-mini-btn { width: 30px; height: 30px; border-radius: 8px; border: none; background: transparent; color: var(--ink-soft); cursor: pointer; display: grid; place-items: center; font-size: .9rem; flex: 0 0 auto; }
.nubia-mini-btn:hover { background: var(--bg); color: var(--accent); }
.nubia-mini-btn .app-icon { width: 16px; height: 16px; }
.nubia-mini-body { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
.nubia-mini .conv-form { flex: 0 0 auto; border-top: 1px solid var(--line-2); }
.nubia-mini .conv-form .btn--coral { padding: .5rem .7rem; }
.nubia-mini .conv-form .btn--coral .app-icon { width: 17px; height: 17px; }
@media (max-width: 560px) { .nubia-fab { right: 1rem; } .nubia-mini { right: 1rem; left: 1rem; width: auto; bottom: 5.85rem; } }

/* ===== Animaciones del sistema de chat =====
   Van DESPUÉS de .mt-unread (1399), .msg-fab-badge (1735) y de los .btn--coral de las dos burbujas
   (1775 / 1824) a propósito: ahí ya está todo definido y ninguna regla posterior puede apagarlas en
   silencio. Ninguna de esas reglas toca animation/opacity/transform, así que no se pisan. */

/* Entrada del mensaje que LLEGA (la clase la pone animateNewBubbles solo en el mensaje nuevo, nunca
   al abrir una conversación ya existente). */
@keyframes bubbleIn { from { opacity: 0; transform: translateY(9px) scale(.97); } to { opacity: 1; transform: none; } }
.bubble--in { animation: bubbleIn .24s var(--ease-out) both; }

/* Rebote del contador de mensajes sin leer, en la barra lateral y en la burbuja. */
@keyframes badgePop { 0% { transform: scale(1); } 45% { transform: scale(1.38); } 100% { transform: scale(1); } }
.mt-unread.is-pop, [data-msg-badge].is-pop, .msg-fab-badge.is-pop { animation: badgePop .42s var(--ease-out); }

/* El botón de enviar se apaga sin nada que enviar. La clase la mantiene syncSendState sobre el
   formulario, no sobre el botón, para poder alcanzar también al de Nubia con un documento adjunto. */
.conv-form .btn--coral { transition: opacity .16s, transform .16s var(--ease-out), filter .16s; }
.conv-form:not(.has-text) .btn--coral { opacity: .42; transform: scale(.93); }

@media (prefers-reduced-motion: reduce) {
  .bubble--in, .mt-unread.is-pop, [data-msg-badge].is-pop, .msg-fab-badge.is-pop { animation: none; }
  .conv-form:not(.has-text) .btn--coral { transform: none; }
}

/* ===== Todo el texto en Raleway (antes serif) del área de miembros, en negrita ===== */
.app-body em, .app-body .serif-em,
.side-avatar, .topbar-title h1, .topbar-avatar, .fp-title, .rail-stats .n, .lb-rank,
.cal-head .m, .evt-when .d, .plan-overview h2, .module-num, .profile-avatar, .profile-id h2,
.profile-stats .row b, .avatar-upload .preview, .kpi .num, .donut .c b, .gate-head h2,
.gate-card h2, .manage-head h3, .onb-intro h2, .coach-avatar, .coach-card h3, .onb-done h2,
.welcome-card h3, .modal-title, .evt-cover-title, .profile-modal h3, .track-title,
.sessions-head-count, .coach-section-title, .coach-student-hero-id h2 { font-weight: 700; }

/* ===== Tamaño de tipografía del área de miembros (pedido del cliente, 02-jul-2026) =====
   Se mantiene la fuente de siempre (Raleway/Inter, sin tocar font-family); solo cambia el TAMAÑO:
   14px para el texto en general, 16px en negrita para los títulos de publicación. Los headers de
   las barras superiores (topbar) NO cambian. No afecta la portada ni "crear perfil / acceder"
   (usan styles.css, no este archivo). */
.app-body * { font-size: 14px !important; }
.topbar-title h1 { font-size: clamp(1.4rem, 3vw, 1.9rem) !important; }
.topbar-title p { font-size: .82rem !important; }
/* Tamaños de publicación (recalibrados 21-jul-2026, pedido cliente): el TÍTULO toma el tamaño que tenía
   el cuerpo (16px) pero en negrita, y el CUERPO baja al tamaño genérico de los textos del portal
   (.app-body * = 14px, el mismo de los boxes del rail como Ranking), sin negrita. */
.fp-cat { font-size: 12px !important; }
.fp-title { font-size: 16px !important; font-weight: 700 !important; }
.fp-body { font-size: 14px !important; }
/* "Visto" del chat: subtexto, más chico que el mensaje. Va acá y con DOS clases (0,2,0) porque
   `.app-body *` de arriba lleva !important y le gana a cualquier regla de una sola clase. */
/* La hora de la burbuja y el separador de día son subtextos: mismo tamaño que el "Visto" y por el
   mismo motivo (dos clases + !important, si no `.app-body *` los deja en 14px, igual que el mensaje). */
.conv-body .bubble time, .msg-mini-body .bubble time { font-size: 11px !important; }
.conv-body .chat-day span, .msg-mini-body .chat-day span { font-size: 11px !important; }
.conv-find .conv-find-count { font-size: 12px !important; }
/* En móvil el mismo tamaño se siente grande: se baja un poco toda la escala (solo ≤560px). Debe ir
   DESPUÉS de las reglas de arriba y repetir su orden (base .app-body * primero, luego los específicos)
   para que la cascada quede igual que en escritorio pero con tamaños menores. No afecta escritorio. */
@media (max-width: 560px) {
  .app-body * { font-size: 13px !important; }
  .topbar-title h1 { font-size: clamp(1.25rem, 4.5vw, 1.7rem) !important; }
  .topbar-title p { font-size: .76rem !important; }
  .fp-cat { font-size: 11px !important; }
  /* Publicaciones en móvil (recalibrado 21-jul): título al tamaño del cuerpo (13px) en negrita, cuerpo al
     genérico del portal (13px, .app-body *). En móvil ambos quedan en 13px; el título se distingue por la negrita. */
  .fp-title { font-size: 13px !important; font-weight: 700 !important; }
  .fp-body { font-size: 13px !important; }
  /* iOS Safari hace ZOOM a toda la página cuando enfocas un campo con font <16px. Al fijar 16px en todo
     campo de escritura (mayor especificidad que `.app-body *`, así que gana pese al 13px !important),
     enfocar deja de disparar ese zoom. Solo crece el texto del propio campo; el resto sigue a 13px.
     Es el arreglo correcto: NO se toca el viewport (pellizcar-para-ampliar sigue disponible). */
  .app-body input, .app-body textarea, .app-body select, .app-body [contenteditable] { font-size: 16px !important; }
}

/* ===== Filtros de píldoras → desplegable de ancho completo en MÓVIL (pedido cliente) — SOLO ≤560px =====
   En móvil las filas de píldoras (tipo de publicación, grupos/comunidades, y los planes de Módulos y
   Cronograma) se ocultan y aparece el MISMO desplegable .members-select pero a lo ancho, respetando
   márgenes. El texto por defecto del composer pasa a su versión corta (una sola línea). Va al final
   para ganar la cascada sobre las reglas base de cada componente. Escritorio queda intacto. */
.fdrop-mobile { display: none; }
.cmp-short { display: none; }
@media (max-width: 560px) {
  .feed-filters,
  .comm-switch .comm-switch-pill,
  .plan-tabs .plan-tab[data-plan],
  .plan-tabs .plan-tab[data-prog-plan] { display: none; }
  .fdrop-mobile { display: flex; width: 100%; box-sizing: border-box; }
  .feed-filter-drop { margin: 0 0 1.1rem; }
  .comm-switch .fdrop-mobile, .plan-tabs .fdrop-mobile { flex: 1 1 auto; min-width: 0; width: auto; }
  .fdrop-mobile > select { flex: 1 1 auto; min-width: 0; width: 100%; }
  .cmp-long { display: none; }
  .cmp-short { display: inline; }
  /* El texto por defecto del composer, en una sola línea (sin envolver) */
  .composer button { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* Contenido de la tarea: el sangrado de 3.4rem (alinea bajo el título en escritorio) se come el 20%
     del ancho en móvil, y el video al 75% (pedido para escritorio) quedaba en ~167px. Juntos dejaban un
     video de ~94px de alto, inusable. En móvil: sin sangrado y video a lo ancho. */
  .task-body { padding-left: .8rem; }
  .task-video { width: 100%; }

  /* ===== Áreas táctiles de 44px (mínimo de la skill mobile-webview-design) =====
     Los controles de icono medían 22-36px. Se agranda el ÁREA TÁCTIL con un pseudo-elemento
     invisible centrado: el icono conserva su tamaño visual y el layout no se mueve.
     Importa especialmente porque estos controles van DENTRO de .task-head, que también es tocable
     (despliega la tarea): un toque impreciso hacía lo contrario de lo buscado. */
  .task-head .lesson-check,
  .task-head .task-edit,
  .task-head .task-del,
  .nubia-mini-btn,
  .conv-form .conv-tool { position: relative; }
  .task-head .lesson-check::after,
  .task-head .task-edit::after,
  .task-head .task-del::after,
  .nubia-mini-btn::after,
  .conv-form .conv-tool::after {
    content: ""; position: absolute; left: 50%; top: 50%;
    width: 44px; height: 44px; transform: translate(-50%, -50%);
  }
  /* Editar y Borrar quedaban a 8.8px: con áreas de 44px se solapaban y borrar es DESTRUCTIVO.
     Se separan para que cada área quede limpia (la skill pide 8px mínimo entre acciones). */
  .task-head .task-edit { margin-right: 1rem; }
  /* Cerrar modal: es absolute, así que agrandarlo no mueve nada. */
  .app-modal-x { width: 44px; height: 44px; }
  /* Campana y burger del topbar. En .icon-btn hay que pisar también el flex-basis (flex:0 0 40px):
     en un contenedor flex el basis manda sobre width. */
  .topbar-burger, .icon-btn { width: 44px; height: 44px; }
  .topbar-actions .icon-btn { flex: 0 0 44px; }

  /* ===== Ventanas emergentes (modales) =====
     Las variantes usan width:min(Xpx, 94vw) = 352px, pero viven dentro de .app-modal que tiene
     padding:1.2rem (caja de 337px): la tarjeta desbordaba ese padding y quedaba PEGADA al borde
     derecho (19px de margen izq. vs 3px der.). width:100% respeta el padding y la deja centrada. */
  .app-modal .app-modal-card { width: 100%; }
  /* Filas [etiqueta][select] de Gestionar miembro: el select tiene min-width:200px, así que a la
     etiqueta le quedaban 85px → rompía en 2 líneas y el nombre del coach salía cortado
     ("Sebastian Alvarez Arena"). En móvil se apilan: etiqueta arriba, select a lo ancho (que además
     es mejor target táctil). */
  .manage-row:has(select), .manage-row:has(.mps) { flex-direction: column; align-items: stretch; gap: .4rem; }
  .manage-row select, .manage-row .mps { min-width: 0; width: 100%; }

  /* Panel de notificaciones: se ancla a la campana (right:0) y su caja de 300px lo empujaba hasta
     6px del borde izquierdo, dejando 69px muertos a la derecha — márgenes rotos respecto al resto
     de la app (que usa 1.2rem). En móvil se ancla al viewport con esos mismos márgenes.
     top: 87px = alto del topbar (77px, medido) + los 10px de separación del diseño original.
     position:fixed funciona aquí: el topbar es sticky y sticky NO crea bloque contenedor para fixed. */
  .notif-panel { position: fixed; top: 87px; left: 1.2rem; right: 1.2rem; width: auto; max-width: none; }
}

/* ===== Compactación móvil · reglas que deben ir AL FINAL para ganar la cascada =====
   `.app-modal-card` (padding 1.6rem, línea ~832) y `.gate-card` (clamp 2rem, línea ~632) se declaran
   más abajo que el bloque de compactación de ≤560px, y con la MISMA especificidad gana la última regla
   del archivo. Por eso su versión móvil vive acá, al final. Escritorio queda intacto. */
@media (max-width: 560px) {
  .app-modal-card { padding: 1.3rem 1.125rem; }                   /* 26px → 18px lateral */
  .gate-card { padding-left: 1.125rem; padding-right: 1.125rem; } /* 32px → 18px (era el mayor del sitio) */
}

/* ===== Nombre de archivo que CONSERVA la extensión al recortarse (chat y adjuntos) =====
   Antes, con solo `text-overflow: ellipsis`, "Guia-de-Marca-Personal-Modulo-3.pdf" se veía
   "Guia-de-Marca-Personal-…" y no se sabía si era un PDF o una planilla. Ahora el cuerpo se corta y la
   extensión queda fija a la derecha: "Guia-de-Marca-Per….pdf". El HTML lo arma fileNameHTML() (app.js).
   Aplica en TODOS los tamaños: los topes de 180/220px también existen en escritorio.
   Especificidad (0,2,0) a propósito, para ganarle a `.bubble-file span` / `.doc-chip-meta b` (0,1,1). */
.fname { display: flex; align-items: baseline; min-width: 0; overflow: hidden; }
.fname .fn-body { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fname .fn-ext { flex: 0 0 auto; white-space: nowrap; }

@media (max-width: 560px) {
  /* Evento del calendario: la celda mide ~40px, así que el título se recortaba a "Sesi…" (ilegible).
     En móvil se muestra un punto coral por evento (hasta 3) y el detalle se lee tocando el día, que
     ahora abre la agenda del día con horario. Los puntos NO son objetivo táctil (7px es imposible de
     acertar): `pointer-events: none` deja que el toque lo reciba la celda completa (28-jul-2026).
     Antes cada `.cal-evt` cubría toda la celda con inset:0 — con varios eventos se apilaban uno sobre
     otro y solo se veía (y se podía tocar) el último. */
  /* En el teléfono la celda SÍ es cuadrada: las píldoras se vuelven puntos, así que sin esto no
     habría nada que le diera alto a la fila y el calendario se aplastaría. */
  .cal-day { aspect-ratio: 1; min-height: 0; }
  .cal-evts { position: absolute; left: 0; right: 0; bottom: 4px; flex-direction: row;
    align-items: center; justify-content: center; gap: 3px; margin: 0; }
  /* El punto va en coral, no en el lavado de la píldora: sobre la celda saldría casi invisible. */
  .cal-evt { width: 6px; height: 6px; padding: 0; border-radius: 50%; background: var(--accent);
    text-indent: -999px; overflow: hidden; flex: 0 0 auto; pointer-events: none; }
  .cal-more { align-self: center; font-size: 9px !important; color: var(--accent-deep); font-weight: 700; }
}

/* ===== Editor de cronograma (admin + coach) =====
   Reutiliza .manage-row + .mod-drag-row + .mod-drag-grip del gestor de planes: un solo patrón de
   "arrastrar para reordenar" en toda la plataforma. Acá solo se agrega lo propio del cronograma. */
.sched-modal { max-width: 900px; }   /* 780 → 900 (ronda de ensanche del 28-jul-2026) */
.sched-total { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; margin: .9rem 0 .2rem; }
.sched-total label { display: grid; gap: .25rem; font-size: .78rem; font-weight: 600; color: var(--ink-soft); }
.sched-total input { width: 92px; padding: .45rem .6rem; border: 1px solid var(--line); border-radius: 10px;
  background: var(--paper); font: inherit; color: var(--ink); }
.sched-list { display: grid; gap: .4rem; margin-top: .9rem; max-height: 52vh; overflow-y: auto; padding-right: .2rem; }
.sched-row.is-off { opacity: .5; }
.sched-see input { width: 17px; height: 17px; accent-color: var(--accent); cursor: pointer; }
.sched-name { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.sched-nums { display: flex; gap: .45rem; flex: 0 0 auto; }
.sched-nums label { display: grid; gap: .15rem; font-size: .66rem; color: var(--ink-mute); text-align: center; }
.sched-nums input { width: 58px; padding: .3rem .35rem; border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg); font: inherit; text-align: center; color: var(--ink); }
@media (max-width: 560px) {
  .sched-row { flex-wrap: wrap; row-gap: .5rem; }
  .sched-name { flex: 1 1 60%; }
  .sched-nums { flex: 1 1 100%; justify-content: flex-start; }
}

/* ============================================================================
   MÓDULOS · menú de 3 puntos, modo Mover y arrastre de tareas (etapa 2C)
   Va al FINAL del archivo a propósito: .mod-cover-card y .task se declaran más
   arriba y con igual especificidad ganaría la última regla. (Ver AUDIT.md,
   "trampa de cascada" — ya costó tres rondas.)
   ============================================================================ */

/* La tarjeta pasa a vivir dentro de un contenedor para poder colgarle el botón
   de 3 puntos por fuera: el botón no puede ir DENTRO de la tarjeta porque la
   tarjeta ya es un <button> y anidar botones es HTML inválido (el clic interno
   se lo comería el externo). El contenedor hereda el ancho fijo del carrusel. */
.mod-cover-wrap { position: relative; flex: 0 0 280px; display: flex; }
.mod-cover-wrap > .mod-cover-card { flex: 1 1 auto; }

.mod-dots { position: absolute; top: .5rem; right: .55rem; z-index: 3; width: 30px; height: 30px;
  border: 0; border-radius: 50%; background: rgba(255,255,255,.92); color: var(--ink);
  display: grid; place-items: center; cursor: pointer; padding: 0;
  box-shadow: 0 2px 8px -1px rgba(28,26,24,.35); opacity: 0; transition: opacity .18s, background .18s; }
.mod-cover-wrap:hover .mod-dots, .mod-dots:focus-visible, .mod-dots.is-open { opacity: 1; }
/* En táctil no hay hover: el botón se ve siempre, si no sería inalcanzable. */
@media (hover: none) { .mod-dots { opacity: 1; } }
.mod-dots:hover { background: #fff; }
.mod-dots .app-icon { width: 16px; height: 16px; }

/* `fixed` y colgado de <body>: dentro de la tarjeta quedaba atrapado en `.mod-track` (overflow-x: auto
   para el carrusel) y un recorte en un eje recorta también el otro, así que el menú se cortaba contra
   el borde de la franja y "Eliminar" desaparecía bajo el cuadro de tareas. Las coordenadas las calcula
   placeModuleMenu() en app.js al abrirlo. z-index alto: tiene que pasar por encima de las tarjetas
   activas (z 2) y del cuadro de tareas. */
.mod-menu { position: fixed; z-index: 200; min-width: 168px; padding: .3rem;
  background: var(--paper); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 18px 44px -18px rgba(28,26,24,.45); }
.mod-menu button { display: flex; align-items: center; gap: .55rem; width: 100%; text-align: left;
  padding: .5rem .6rem; border: 0; background: none; border-radius: 8px; cursor: pointer;
  font: 500 .84rem/1.2 var(--sans); color: var(--ink); }
.mod-menu button:hover { background: var(--bg-2); }
.mod-menu button.is-danger { color: #c0392b; }
.mod-menu .app-icon { width: 15px; height: 15px; opacity: .75; }
.mod-menu hr { border: 0; border-top: 1px solid var(--line-2); margin: .25rem .15rem; }

/* ---- Modo Mover: todo el plan entra en edición ---- */
.mods-move-bar { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  background: var(--ink); color: var(--paper); border-radius: 12px; padding: .6rem .8rem; margin-bottom: .2rem;
  font: 500 .84rem/1.3 var(--sans); }
.mods-move-bar b { font-weight: 700; }
.mods-move-bar .mmb-hint { opacity: .75; }
.mods-move-bar .mmb-sp { flex: 1 1 auto; }
.mods-move-bar .app-icon { width: 16px; height: 16px; }

.mods-moving .mod-dots { display: none; }
/* CRÍTICO para el auto-scroll: sin esto, `scroll-snap-type: x proximity` (del .mod-track) devuelve a 0
   cada micro-incremento de 7px que hace el RAF → el carrusel queda pinneado y el auto-scroll "no mueve"
   aunque el trigger sí dispare. En modo Mover se apaga el snap; al salir se restaura solo. */
.mods-moving .mod-track { scroll-snap-type: none; }
/* Reorden de módulos: la tarjeta arrastrada queda atenuada en su sitio y solo se abre una separación
   animada entre las dos tarjetas donde caería (indicador de hueco, sin reordenar en vivo). */
.mods-moving .mod-cover-wrap { transition: margin .16s ease; }
.mods-moving .mod-cover-wrap.mod-gap-before { margin-left: 2.4rem; }
.mods-moving .mod-cover-wrap.mod-gap-after { margin-right: 2.4rem; }
.mods-moving .mod-cover-card { cursor: grab; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent-soft); }
.mods-moving .mod-cover-card:hover { transform: none; }
.mods-moving .mod-cover-card.is-active { transform: none; }
.mods-moving .mod-cover-wrap.dragging { opacity: .4; }
/* (Antes había una grip/hamburguesa en modo Mover; se quitó — la tarjeta queda limpia y el borde coral +
   cursor grab + la barra "Moviendo módulos" ya indican que se arrastra.) */

/* ---- Arrastre de tareas dentro del módulo ---- */
.task-grip { flex: 0 0 auto; width: 26px; height: 26px; display: grid; place-items: center;
  color: var(--ink-mute); cursor: grab; opacity: 0; transition: opacity .18s; }
.task:hover .task-grip, .task-grip:focus-visible { opacity: .8; }
@media (hover: none) { .task-grip { opacity: .55; } }
.task-grip .app-icon { width: 15px; height: 15px; }
.task.dragging { opacity: .4; }
.module-lessons.is-reordering .task { cursor: grab; }
/* Modo mover encendido: las asas dejan de esconderse, para que se vea de dónde hay que agarrar. */
.module-lessons.is-move-mode .task-grip { opacity: 1; color: var(--accent-deep); }

/* ---- Editor de cronograma · una fila por módulo, desplegable ---------------
   Mismo patrón que .task en Módulos: UNA caja redondeada por módulo, con la
   cabecera y el panel adentro. Redondear dos elementos hermanos por separado
   deja siempre alguna esquina en ángulo recto.
   La cabecera es un <button> aparte del asa de arrastre: si toda la fila fuera
   pinchable, arrastrar dispararía también el desplegado. */
.sched-item { border-radius: 12px; overflow: hidden; transition: background .2s; }
.sched-item.is-open { background: var(--bg-2); }
.sched-item:not(.is-open):hover { background: var(--bg); }
.sched-item.dragging { opacity: .4; }

/* Separador entre módulos plegados. Se retira alrededor del abierto: contra su
   fondo beige, la línea se leería como el borde de otra caja. */
.sched-item + .sched-item > .sched-row { border-top: 1px solid var(--line-2); }
.sched-item.is-open > .sched-row,
.sched-item.is-open + .sched-item > .sched-row { border-top-color: transparent; }

.sched-item > .sched-row { padding: .15rem .7rem; border-radius: 0; }
.sched-row .sched-head { flex: 1 1 auto; display: flex; align-items: center; gap: .6rem;
  min-width: 0; padding: .5rem 0; border: 0; background: none; cursor: pointer; text-align: left; font: inherit; color: inherit; }
.sched-row .sched-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.sched-row .sched-when { flex: 0 0 auto; font-size: .78rem; color: var(--ink-mute); }
.sched-caret { flex: 0 0 auto; width: 15px; height: 15px; color: var(--ink-mute); transition: transform .2s var(--ease-out); }
.sched-item.is-open .sched-caret { transform: rotate(180deg); }

.sched-panel { display: flex; gap: .9rem; flex-wrap: wrap; padding: .2rem 1rem 1rem 2.5rem; }
.sched-panel label { display: flex; flex-direction: column; gap: .25rem; font-size: .78rem; color: var(--ink-mute); }
/* Mismos valores que el campo "Semanas" de este mismo modal (.sched-total input): sin declarar
   padding, el número quedaba pegado al borde izquierdo. */
.sched-panel input { width: 7.5rem; padding: .45rem .7rem; border: 1px solid var(--line);
  border-radius: 10px; background: var(--paper); font: inherit; color: var(--ink); }

@media (max-width: 560px) {
  .sched-row .sched-when { display: none; }   /* el rango de días ya se ve al desplegar */
  .sched-panel { padding-left: 1rem; }
  .sched-panel input { width: 100%; }
  .sched-panel label { flex: 1 1 8rem; }
}

/* ---- Campo de redacción que crece con el texto (las 4 superficies) --------
   Era <input>, que no puede tener más de una línea. Ahora es <textarea> con
   alto automático: arranca en una línea y crece hasta cinco (el tope lo aplica
   autoGrow en app.js, que es quien puede medir el contenido real).
   `resize: none` porque el tirador nativo de la esquina pelearía con el alto
   calculado; `field-sizing` no se usa todavía por soporte disparejo. */
.conv-form textarea,
.conv-form .conv-input {
  resize: none; overflow-y: hidden; min-height: 0;
  line-height: 1.35; font-family: inherit;
}
/* El formulario deja de centrar verticalmente: con el campo creciendo, los
   botones deben quedar abajo, a la altura de la última línea. */
.conv-form { align-items: flex-end; }

/* ---- Menú de adjuntos (3 puntos) en las 4 barras de redacción -------------
   Imagen y documento se agrupan acá; el micrófono queda fuera, con clic directo,
   porque es la acción que más se usa. En las burbujas no cabían los tres sueltos. */
.conv-more-wrap { position: relative; flex: 0 0 auto; }
.conv-att-menu { position: absolute; bottom: calc(100% + .45rem); left: 0; z-index: 8; min-width: 172px;
  padding: .3rem; background: var(--paper); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 16px 40px -16px rgba(28,26,24,.45); }
.conv-att-menu button { display: flex; align-items: center; gap: .55rem; width: 100%; text-align: left;
  padding: .5rem .6rem; border: 0; background: none; border-radius: 8px; cursor: pointer;
  font: 500 .85rem/1.2 var(--sans); color: var(--ink); }
.conv-att-menu button:hover { background: var(--bg-2); }
.conv-att-menu .app-icon { width: 15px; height: 15px; opacity: .75; }

/* Dentro de la burbuja de Mensajes la barra es oscura: el menú se aclara para no desaparecer. */
.msg-mini-form .conv-att-menu { background: #262220; border-color: rgba(255,255,255,.14); }
.msg-mini-form .conv-att-menu button { color: #f4f1ea; }
.msg-mini-form .conv-att-menu button:hover { background: rgba(255,255,255,.09); }
.msg-mini-form .conv-tool { color: rgba(244,241,234,.72); }
.msg-mini-form .conv-tool:hover { background: rgba(255,255,255,.1); color: #f4f1ea; }

/* ---- Correcciones de la barra oscura y del menú "⋮" de las burbujas -------

   1) Contraste de los botones. `.conv-tool` trae fondo crema y borde claro,
      pensados para la barra clara del área grande. Sobre la barra negra de la
      burbuja quedaban como dos círculos blancos sin icono visible. */
.msg-mini-form .conv-tool {
  background: rgba(255,255,255,.1);
  border-color: rgba(255,255,255,.18);
  color: rgba(244,241,234,.82);
}
.msg-mini-form .conv-tool:hover {
  background: rgba(255,255,255,.18);
  border-color: rgba(255,255,255,.3);
  color: #f4f1ea;
}
/* Grabando: se mantiene el coral, que sí contrasta contra el negro. */
.msg-mini-form .conv-tool.is-rec { background: var(--accent); border-color: var(--accent); color: #fff; }

/* 2) El menú "⋮" de una burbuja (descargar / eliminar) se posiciona en
      coordenadas de pantalla al abrirse — ver bindBubbleActions—, porque la
      conversación hace scroll y recortaba el panel. */
.bubble-menu-panel.is-floating { position: fixed; bottom: auto; right: auto; z-index: 200; }

/* ---------- Vista previa de adjuntos del chat (popup centrado, 27-jul-2026) ----------
   La imagen y el documento del chat abren acá en grande. La ficha del modal se estira sola
   (att-prev-modal) para aprovechar la pantalla sin llegar a los bordes. */
.att-prev-modal { max-width: min(900px, 94vw); width: 100%; }
.att-prev { display: flex; flex-direction: column; gap: .8rem; min-width: 0; }
.att-prev-head { display: flex; align-items: center; gap: .8rem; justify-content: space-between; padding-right: 2.2rem; }
.att-prev-head .fname { font: 600 .92rem var(--sans); color: var(--ink); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.att-prev-img { display: block; max-width: 100%; max-height: 74vh; margin: 0 auto; border-radius: 12px; object-fit: contain; background: var(--bg-2); }
.att-prev-frame { width: 100%; height: 74vh; border: 1px solid var(--line); border-radius: 12px; background: var(--bg-2); }
.att-prev-none { display: flex; flex-direction: column; align-items: center; gap: .7rem; text-align: center; padding: 2.4rem 1rem; background: var(--bg-2); border-radius: 12px; }
.att-prev-none .app-icon { width: 34px; height: 34px; color: var(--ink-mute); }
.att-prev-none b { font: 600 1rem var(--sans); color: var(--ink); word-break: break-word; }
.att-prev-none p { margin: 0; max-width: 380px; font-size: .86rem; color: var(--ink-mute); }
/* La imagen y el documento de la burbuja ahora se pulsan → se avisa con el cursor. El documento pasó
   de <a> a <button>, así que hay que neutralizar el estilo propio del botón (fuente y alineación). */
.bubble-img { cursor: zoom-in; }
.bubble-file { cursor: pointer; font: inherit; text-align: left; width: auto; }
.bubble-file:hover { border-color: var(--accent); }

/* ---------- Historial de pausas (línea de tiempo tipo seguimiento de envío, 27-jul-2026) ----------
   Solo en la vista del coach sobre el cronograma de un alumno. Lo más reciente arriba: cada entrada
   lleva un punto en la línea vertical y el desenlace real de esa solicitud. */
.pause-hist { list-style: none; margin: .2rem 0 0; padding: 0 0 0 1.35rem; position: relative; }
.pause-hist::before { content: ""; position: absolute; left: .3rem; top: .45rem; bottom: .45rem; width: 2px; background: var(--line-2); border-radius: 2px; }
.ph-item { position: relative; padding: 0 0 1rem; }
.ph-item:last-child { padding-bottom: 0; }
/* El punto: relleno para la entrada más reciente, hueco para las anteriores. */
.ph-item::before { content: ""; position: absolute; left: -1.35rem; top: .3rem; width: 12px; height: 12px; border-radius: 50%; background: var(--paper); border: 2px solid var(--line); box-sizing: border-box; }
.ph-item:first-child::before { border-color: var(--accent); background: var(--accent); }
.ph-head { display: flex; align-items: baseline; gap: .6rem; justify-content: space-between; }
.ph-head b { font: 600 .88rem var(--sans); color: var(--ink); }
.ph-head time { flex: 0 0 auto; font-size: .74rem; color: var(--ink-mute); }
.ph-item p { margin: .15rem 0 0; font-size: .84rem; color: var(--ink-soft); line-height: 1.45; }
.ph-reason { font-style: italic; color: var(--ink-mute) !important; }
.ph-ask { display: block; margin-top: .3rem; font-size: .72rem; color: var(--ink-mute); }
/* Color del estado, sin gritar: solo el título cambia de tono. */
.ph-item.is-live .ph-head b { color: var(--accent-deep); }
.ph-item.is-cut  .ph-head b { color: var(--accent-deep); }
.ph-item.is-done .ph-head b { color: #2E7D5B; }
.ph-item.is-pend .ph-head b { color: var(--ink); }
.ph-item.is-off  .ph-head b { color: var(--ink-mute); }
.ph-item.is-off p, .ph-item.is-off .ph-ask { opacity: .85; }

/* ---- Gestor de conjuntos en móvil (3-ago-2026) ----------------------------
   Al añadir «Duplicar» pasaron a ser TRES botones, y a 375 px las acciones se
   comían la mitad del ancho: el nombre se partía en cuatro líneas y cada fila
   subía de 102 a 145 px (con siete conjuntos, 300 px más de scroll).
   A partir de aquí el bloque de acciones baja bajo el nombre y ocupa el ancho.
   Va AL FINAL DEL ARCHIVO a propósito: `.manage-row` (línea ~1242) tiene la
   misma especificidad, así que un override colocado antes se ignoraría sin
   avisar — la trampa de cascada que ya mordió en este proyecto. */
.set-row-id      { min-width: 0; flex: 1; }
/* Marca de conjunto OCULTO en el gestor (fase2/105). El equipo lo sigue viendo y editando; esta
   píldora es lo único que lo distingue de uno normal, así que va sobria pero legible. */
.set-oculto {
  display: inline-flex; align-items: center; gap: .25rem;
  margin-left: .45rem; padding: .1rem .45rem; border-radius: 999px;
  background: var(--line); color: var(--ink-soft);
  font-size: .68rem; font-weight: 600; vertical-align: middle;
}
.set-oculto .app-icon { width: .7rem; height: .7rem; }
.set-row-acciones { display: flex; gap: .4rem; flex: 0 0 auto; }

@media (max-width: 560px) {
  [data-set-row] { flex-wrap: wrap; row-gap: .5rem; }
  .set-row-acciones { flex: 1 0 100%; justify-content: flex-end; }
}
