/* ============================================================================
   portal-v3.css — E1 del rediseño del portal médico: SISTEMA VISUAL.
   Se carga SOLO cuando el servidor manda portal_v3:true para la médica
   (politica_portal.php → auth.php → medico.js agrega body.portal-v3 y este CSS).
   TODO va bajo body.portal-v3: sin la clase, esta hoja es inerte.

   Regla del rediseño: NO se toca ningún id ni ninguna función. Esto es piel.

   Paleta ELEGIDA POR MAURICIO (28-jul), en su orden:
     receta #70d6ff · orden #ff70a6 · certificado #ff9770 ·
     control #ffd670 · GES #e9ff70
   El texto de cada color NO es blanco: es un tono casi negro del MISMO matiz,
   calculado para superar 7:1 sobre su relleno (con blanco ninguno pasa de 2:1).
   Ratios verificados: 7.48 · 7.22 · 7.28 · 7.35 · 7.38.
   ============================================================================ */

body.portal-v3 {
  --v3-receta:      #70d6ff;  --v3-receta-txt:  #023950;  --v3-receta-suave:  #ebf9ff;
  --v3-orden:       #ff70a6;  --v3-orden-txt:   #2d0112;  --v3-orden-suave:   #ffebf2;
  --v3-cert:        #ff9770;  --v3-cert-txt:    #461402;  --v3-cert-suave:    #fff0eb;
  --v3-control:     #ffd670;  --v3-control-txt: #553d02;  --v3-control-suave: #fff9eb;
  --v3-ges:         #e9ff70;  --v3-ges-txt:     #485502;  --v3-ges-suave:     #fcffeb;
}

/* ── Los puntos de entrada de cada documento, con su color de relleno ──────
   Ids REALES del portal (verificados contra medico.js del 28-jul):
     #mf-emitir-rx / #mf-emitir-rx-add  → receta
     #mf-emitir-or / #mf-emitir-or-add  → orden de exámenes
     #hv-cert                           → certificado (hoja de vida)
     #hv-ges                            → notificación GES (hoja de vida)
   El botón era .btn--ghost (blanco con borde). Pasa a relleno completo con
   borde y texto del tono oscuro; mismo tamaño, mismo id, mismo handler.     */
body.portal-v3 #mf-emitir-rx,
body.portal-v3 #mf-emitir-rx-add {
  background: var(--v3-receta) !important;
  border: 2px solid var(--v3-receta-txt) !important;  /* m1: button{border:none} en estilos.css hacía mudo el border-color */
  color: var(--v3-receta-txt) !important;
}
body.portal-v3 #mf-emitir-or,
body.portal-v3 #mf-emitir-or-add {
  background: var(--v3-orden) !important;
  border: 2px solid var(--v3-orden-txt) !important;
  color: var(--v3-orden-txt) !important;
}
body.portal-v3 #hv-cert {
  background: var(--v3-cert) !important;
  border: 2px solid var(--v3-cert-txt) !important;
  color: var(--v3-cert-txt) !important;
}
body.portal-v3 #hv-ges {
  background: var(--v3-ges) !important;
  border: 2px solid var(--v3-ges-txt) !important;
  color: var(--v3-ges-txt) !important;
}
/* El ícono de dentro hereda el color del texto (los SVG van con currentColor). */
body.portal-v3 #mf-emitir-rx svg, body.portal-v3 #mf-emitir-or svg,
body.portal-v3 #hv-cert svg,      body.portal-v3 #hv-ges svg { color: inherit; }

/* Hover: mismo color, elevación — no cambia el matiz para no romper el mapa. */
body.portal-v3 #mf-emitir-rx:hover, body.portal-v3 #mf-emitir-or:hover,
body.portal-v3 #hv-cert:hover,      body.portal-v3 #hv-ges:hover,
body.portal-v3 #mf-emitir-rx-add:hover, body.portal-v3 #mf-emitir-or-add:hover {
  filter: brightness(1.04);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(30, 26, 63, .18);
}

/* ── Acentos de las pantallas de documento ya existentes ──────────────────
   #pantalla-certificado y #pantalla-ges viven en medico.html (28-jul).
   Solo acento de cabecera: el contenido no se toca (el CSS de la PLANTILLA
   del PDF está prohibido: se imprime dentro de cada documento emitido).    */
body.portal-v3 #pantalla-certificado h1,
body.portal-v3 #pantalla-certificado h2 { color: var(--v3-cert-txt); }
body.portal-v3 #pantalla-ges h1,
body.portal-v3 #pantalla-ges h2 { color: var(--v3-ges-txt); }

/* ── Detalle de prestaciones: NO es uno de los 5 documentos de la paleta.
   Se deja neutro a propósito para no inventar un sexto color. ─────────── */

/* ── E2 · Los botones de emisión DE LA SALA, con el mismo mapa de color ───
   pintarReceta() genera #rx-dl-receta / #rx-dl-orden / #rx-cert-abrir /
   #rx-ges-abrir dentro de #receta-emision (medico.js ~5493-5511). Mismo
   método que E1: relleno completo + borde y texto del tono oscuro (≥7:1). */
body.portal-v3 #rx-dl-receta {
  background: var(--v3-receta) !important;
  border: 2px solid var(--v3-receta-txt) !important;  /* m1: button{border:none} en estilos.css hacía mudo el border-color */
  color: var(--v3-receta-txt) !important;
}
body.portal-v3 #rx-dl-orden {
  background: var(--v3-orden) !important;
  border: 2px solid var(--v3-orden-txt) !important;
  color: var(--v3-orden-txt) !important;
}
body.portal-v3 #rx-cert-abrir {
  background: var(--v3-cert) !important;
  border: 2px solid var(--v3-cert-txt) !important;
  color: var(--v3-cert-txt) !important;
}
body.portal-v3 #rx-ges-abrir {
  background: var(--v3-ges) !important;
  border: 2px solid var(--v3-ges-txt) !important;
  color: var(--v3-ges-txt) !important;
}
body.portal-v3 #rx-dl-receta svg,  body.portal-v3 #rx-dl-orden svg,
body.portal-v3 #rx-cert-abrir svg, body.portal-v3 #rx-ges-abrir svg { color: inherit; }
body.portal-v3 #rx-dl-receta:hover,  body.portal-v3 #rx-dl-orden:hover,
body.portal-v3 #rx-cert-abrir:hover, body.portal-v3 #rx-ges-abrir:hover {
  filter: brightness(1.04);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(30, 26, 63, .18);
}

/* ── FASE 55 (§5.7): SE RETIRA la regla E2 de #receta-emision bajo el video.
   El nodo volvió a la columna derecha (desplegable "Documentos de la
   consulta"); bajo el video quedan las tarjetas de #v3-acciones. ────────── */

/* M2 (Renata): con las tarjetas bajo el video, el escenario a 74vh dejaba la
   emisión a una pantalla entera de scroll y usarla sacaba el video de vista.
   En v3 el escenario baja a 56vh: lo alto de las tarjetas asoma sin scroll.
   ⚠️ MEDIR EN NAVEGADOR antes del deploy definitivo (deuda declarada de QA). */
body.portal-v3 .video-escenario.is-jitsi { height: 56vh; min-height: 400px; }
/* O3 medido (arnés remoto/harness-e2.html, 29-jul): en notebooks bajos el
   min-height de 400px + la cabecera de la sala (~180px) + la barra de
   controles dejaban las tarjetas justo bajo el pliegue (top 724px con 672 de
   viewport). En pantallas de poca ALTURA el escenario cede un poco más. */
@media (max-height: 760px) {
  body.portal-v3 .video-escenario.is-jitsi { height: 46vh; min-height: 300px; }
}

/* ── F58 · La malla del mockup: 1.4fr / 1.1fr (sala-a.html:32) ────────────
   El clásico reparte 50/50 (estilos.css @min-901); el mockup le da más carril
   al video. minmax(0,…) OBLIGATORIO — la lección del 50/50: un track 1fr es
   minmax(auto,1fr) y el escenario de 74vh infla la columna. La regla del
   cajón abierto (v3-cajon-abierto, con !important) sigue ganando. */
@media (min-width: 901px) {
  /* F58d: también en ESPERA (sin .modo-jitsi) — la base del clásico es
     1.02fr/1.25fr (estilos.css:1073, panel MÁS ancho) y angostaba las
     tarjetas antes de admitir a la paciente. El mockup no distingue estados. */
  body.portal-v3 .sesion-sala {
    grid-template-columns: minmax(0, 1.4fr) minmax(0, 1.1fr);
  }
}

/* F58b · sala-a.html:33: bajo 1181px la malla del mockup colapsa a UNA columna
   (video arriba, panel abajo). Sin esto, la banda 901-1180 combinaba malla de
   2 columnas con 3 tarjetas por fila y dejaba la tarjeta en ~127px (O-1 de
   Renata). Va DESPUÉS de la media min-901 para ganarle en la banda compartida;
   la del cajón abierto (!important) le sigue ganando a ambas. */
@media (max-width: 1180px) {
  body.portal-v3 .sesion-sala { grid-template-columns: 1fr; }
}

/* ── E2 · La malla "carril del video + cajón" (mockup aprobado) ───────────
   Fiel a sala-a.html: NADA flota sobre el video ni lo atenúa. Con el cajón
   abierto (clase v3-cajon-abierto que pone abrirCajonV3 en .sesion-sala):
   video angosto y sticky a la izquierda, cajón como carril derecho, y se
   ocultan el panel de pestañas y las tarjetas — igual que en el mockup.
   El nodo #v3-cajon lo muda activarPortalV3() dentro de .sesion-sala. */
/* ⚠️ Solo en ancho: bajo 900px el bloque de más abajo la devuelve a 1 columna
   (hallazgo O1 de Renata: sin eso, el !important le ganaba TAMBIÉN al colapso
   móvil de estilos.css:1114 y el cajón quedaba en ~34px en un teléfono). */
body.portal-v3 .sesion-sala.v3-cajon-abierto {
  grid-template-columns: minmax(300px, 420px) minmax(0, 1fr) !important;
}
/* O2 (declarado a propósito): en v3 el escenario baja a 56vh pero el panel
   conserva sus 74vh — el invariante "panel = video" del portal clásico
   (estilos.css:1144) queda RETIRADO para v3: acá las tarjetas viven bajo el
   video y las columnas ya no son simétricas. El tope de 74vh del panel (el
   innegociable de medico.js) NO se toca. */
/* m10 (diferencia consciente con el mockup): #trx-box sigue visible con el
   cajón abierto — la transcripción pertenece a la LLAMADA, no a las tarjetas. */
body.portal-v3 .v3-cajon-abierto .sesion-panel,
body.portal-v3 .v3-cajon-abierto #receta-emision { display: none; }
body.portal-v3 .v3-cajon-abierto .sesion-video {
  position: sticky !important;   /* gana al position:static del modo-jitsi (estilos.css:1137) */
  top: 16px;
  align-self: start;
}
body.portal-v3 .v3-cajon-abierto .video-escenario.is-jitsi {
  height: 260px;                 /* mismo alto que el mockup: visible, sin dominar */
  min-height: 260px;
}
body.portal-v3 #v3-cajon {
  align-self: start;
  position: sticky;
  top: 16px;
  max-height: calc(100vh - 32px);  /* scroll interno: el video no se va de pantalla */
}
@media (max-width: 900px) {
  /* O1: la malla vuelve EXPLÍCITAMENTE a 1 columna (la regla de arriba lleva
     !important y sin esto también ganaba en angosto). El cajón queda BAJO el
     video, que se achica para no empujar el formulario fuera de pantalla. */
  body.portal-v3 .sesion-sala.v3-cajon-abierto { grid-template-columns: 1fr !important; }
  body.portal-v3 .v3-cajon-abierto .sesion-video { position: static !important; }
  body.portal-v3 .v3-cajon-abierto .video-escenario.is-jitsi {
    height: 200px;
    min-height: 200px;
  }
  body.portal-v3 #v3-cajon { position: static; max-height: none; }
}


/* F72A-F73B-APLICADO — centinela del parcheador: si esta cadena está, las
   fases 72a/72b/73a/73b ya se aplicaron y el parche se niega a repetirse. */

/* ══════════════════════════════════════════════════════════════════════════
   §F72a · COLOCACIÓN DE LA TRANSCRIPCIÓN EN LA MALLA DE LA SALA
   ══════════════════════════════════════════════════════════════════════════

   🔴 ESTAS REGLAS NACEN INERTES, A PROPÓSITO.
   Hoy `#trx-box` es hijo de `.sesion-video` (medico.html), así que NO es un
   ítem de la malla y ningún selector de acá abajo coincide con nada. Otra
   sesión lo va a mudar para que sea HERMANO, dentro de `.sesion-sala`. Recién
   ahí esto se enciende, y se enciende SOLO.
   El interruptor es `:has(> #trx-box)`: mientras el nodo no sea hijo directo,
   la condición es falsa y el archivo se comporta exactamente como hoy. No es
   elegancia: es lo que permite desplegar el CSS ANTES que el JS sin abrir una
   ventana en la que la sala se vea rota.

   🔴 POR QUÉ HAY QUE DECLARAR TAMBIÉN LA FILA, Y NO SOLO LA COLUMNA
   Con `grid-column` en `#trx-box` y en `.sesion-panel` y nada en
   `.sesion-video`, el algoritmo de autocolocación procesa PRIMERO los ítems
   con columna definida: la transcripción se llevaría la fila 1 y el VIDEO
   caería a la fila 2. O sea, la transcripción arriba y la paciente abajo.
   Por eso los tres van con fila y columna explícitas: así el resultado no
   depende del orden en que la otra sesión inserte el nodo.

   🔴 POR QUÉ CADA ANCHO SE REDECLARA
   `.sesion-sala` tiene CINCO declaraciones de malla compitiendo (estilos.css
   1073 / 1114 / 1144, y acá arriba la de min-901, la de max-1180 y la del
   cajón con !important). En los estados de UNA sola columna, dejar un
   `grid-column: 2` heredado CREARÍA una segunda columna implícita y rompería
   el colapso móvil. Por eso cada banda repite su colocación completa.

   ⚠️ `:has()` es la condición de encendido. Un navegador que no lo entienda
   descarta la regla entera: la sala vuelve a la autocolocación, que tras la
   mudanza es incorrecta. Chrome/Edge 105+, Safari 15.4+, Firefox 121+ lo
   soportan; esta hoja YA depende de `::details-content`, que es bastante más
   nuevo. Queda declarado, no escondido. */

/* ── A · Dos columnas (≥1181px, sin cajón): video arriba, transcripción
   debajo del video, panel a la derecha ocupando las dos filas. Así la
   transcripción queda pegada al video —pertenece a la LLAMADA— y no le roba
   alto a la columna de trabajo. */
body.portal-v3 .sesion-sala:has(> #trx-box) > .sesion-video { grid-column: 1; grid-row: 1; }
body.portal-v3 .sesion-sala:has(> #trx-box) > #trx-box      { grid-column: 1; grid-row: 2; }
body.portal-v3 .sesion-sala:has(> #trx-box) > .sesion-panel { grid-column: 2; grid-row: 1 / span 2; }

/* ── B · Una sola columna (≤1180px, que cubre la banda 901-1180 de la F58b y
   el colapso móvil de ≤900). Todo apilado; NADIE pide la columna 2. */
@media (max-width: 1180px) {
  body.portal-v3 .sesion-sala:has(> #trx-box) > .sesion-video { grid-column: 1; grid-row: 1; }
  body.portal-v3 .sesion-sala:has(> #trx-box) > #trx-box      { grid-column: 1; grid-row: 2; }
  body.portal-v3 .sesion-sala:has(> #trx-box) > .sesion-panel { grid-column: 1; grid-row: 3; }
}

/* ── C · Cajón abierto, en ancho. Acá `.sesion-panel` está en display:none
   (regla §1.6) y por lo tanto NO es ítem de la malla: los ítems son el video,
   la transcripción y `#v3-cajon` (que activarPortalV3 cuelga de `.sesion-sala`
   con appendChild, medico.js:11638 — es hijo DIRECTO, verificado).
   Estas reglas llevan una clase más que las de A, así que le ganan por
   especificidad en cualquier orden. */
body.portal-v3 .sesion-sala.v3-cajon-abierto:has(> #trx-box) > .sesion-video { grid-column: 1; grid-row: 1; }
body.portal-v3 .sesion-sala.v3-cajon-abierto:has(> #trx-box) > #trx-box      { grid-column: 1; grid-row: 2; }
body.portal-v3 .sesion-sala.v3-cajon-abierto:has(> #trx-box) > #v3-cajon     { grid-column: 2; grid-row: 1 / span 2; }

/* ── D · Cajón abierto en angosto: la malla vuelve a 1 columna (O1 de Renata)
   y el cajón baja bajo la transcripción. */
@media (max-width: 900px) {
  body.portal-v3 .sesion-sala.v3-cajon-abierto:has(> #trx-box) > .sesion-video { grid-column: 1; grid-row: 1; }
  body.portal-v3 .sesion-sala.v3-cajon-abierto:has(> #trx-box) > #trx-box      { grid-column: 1; grid-row: 2; }
  body.portal-v3 .sesion-sala.v3-cajon-abierto:has(> #trx-box) > #v3-cajon     { grid-column: 1; grid-row: 3; }
}

/* 🔴 PREGUNTA ABIERTA QUE **NO** RESUELVO ACÁ (va en el informe, para Mauricio
   y Renata). El comentario m10 de más arriba dice que `#trx-box` sigue visible
   con el cajón abierto porque «la transcripción pertenece a la LLAMADA». Esa
   decisión se tomó cuando el nodo colgaba del video. Tras la mudanza pasa a
   COMPETIR como ítem de malla con `#v3-cajon`, que es `sticky` con
   `max-height: calc(100vh - 32px)`. Las reglas C y D conservan la intención
   actual (cajón a la derecha, transcripción bajo el video), pero si el
   resultado en pantalla no convence, eso es DISEÑO y lo define Josefa: no lo
   invento yo en una hoja de estilos. */
/* ══════════════════════════ E3 ══════════════════════════ */

/* ── E3 · Control de transcripción en la barra (junto a Finalizar) ────────
   #v3-trx-btn nace hidden en medico.html y solo lo muestra v3TrxMostrar()
   cuando la transcripción arrancó. Borde visible: es un control serio, pero
   NO rojo — el rojo es de "Finalizar consulta" (irreversible); confundirlos
   sería el riesgo de foco/teclado anotado en el plan aprobado. */
body.portal-v3 #v3-trx-btn {
  border: 2px solid #2a2440 !important;
  background: #fff !important;
  color: #2a2440 !important;
  font-weight: 600;
}
body.portal-v3 #v3-trx-ayuda {
  border: 2px solid #5b4fcf !important;
  background: #fff !important;
  color: #5b4fcf !important;
  border-radius: 50%;
  width: 34px; height: 34px;
  min-height: 34px;
  padding: 0;
  font-weight: 700;
}

/* ── E3 · Calendario del próximo control ("paupérrimo" → legible) ─────────
   El markup lo genera pcPintar() (.pc-sem solo existe en v3): grilla de 7,
   días con cupo en el color CONTROL de la paleta (#ffd670 / texto #553d02),
   día activo con relleno completo, horas como chips. Solo bajo body.portal-v3:
   el calendario clásico de Margot no cambia ni un píxel. */
body.portal-v3 .pc-sem {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
  margin-bottom: 4px;
  text-align: center;
  font-size: .72rem;
  font-weight: 700;
  color: #6b6580;
}
body.portal-v3 .pc-cal {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 4px;
}
body.portal-v3 .pc-dia {
  aspect-ratio: 1;
  min-height: 34px;
  border-radius: 10px;
  border: 1px solid #e5e0f0;
  background: #fff;
  font-size: .85rem;
  position: relative;
}
body.portal-v3 .pc-dia--off { border: none; background: transparent; }
body.portal-v3 .pc-dia.sin-cupo { color: #b9b3c8; background: #faf9fd; }
body.portal-v3 .pc-dia.hay-cupo {
  background: var(--v3-control-suave);
  border: 1px solid var(--v3-control);
  color: var(--v3-control-txt);
  font-weight: 700;
  cursor: pointer;
}
body.portal-v3 .pc-dia.hay-cupo::after {
  content: "";
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 4px;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--v3-control-txt);
}
body.portal-v3 .pc-dia.hay-cupo:hover {
  /* m4 (Renata): historia.css ponía lavanda + texto blanco en el hover y
     sacaba el día del mapa de color "control". Se pisa completo. */
  background: var(--v3-control) !important;
  color: var(--v3-control-txt) !important;
  transform: translateY(-1px);
}
body.portal-v3 .pc-dia.activo {
  background: var(--v3-control) !important;
  border: 2px solid var(--v3-control-txt) !important;
  color: var(--v3-control-txt) !important;
}
body.portal-v3 .pc-horas { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
body.portal-v3 .pc-hora {
  border: 1px solid #d9d3ea;
  background: #fff;
  border-radius: 999px;
  padding: 6px 14px;
  font-size: .85rem;
  cursor: pointer;
}
body.portal-v3 .pc-hora:hover {
  border-color: var(--v3-control-txt);
  color: var(--v3-control-txt) !important;   /* m4: historia.css dejaba el violeta de marca */
  background: #fff !important;
}
body.portal-v3 .pc-hora.activa {
  background: var(--v3-control) !important;
  border: 2px solid var(--v3-control-txt) !important;
  color: var(--v3-control-txt) !important;
  font-weight: 700;
}

/* ══════════════════════════ E4 ══════════════════════════ */

/* ── E4 · MRS con los textos del visado de Dominga (29-jul) ──────────────
   Las clases .v3-mrs-* solo las genera el JS cuando v3MrsActivo(): en el
   portal clásico no existen. Detalle de subescala como segunda línea del
   nombre; rótulo de fuente y leyenda como letra chica honesta (visible,
   no escondida). */
body.portal-v3 .v3-mrs-det {
  display: block;
  font-size: .72rem;
  font-weight: 400;
  color: #6b6580;
  line-height: 1.35;
  margin-top: 1px;
}
body.portal-v3 .v3-mrs-fuente,
body.portal-v3 .v3-mrs-leyenda {
  font-size: .74rem;
  color: #6b6580;
  line-height: 1.45;
  margin: 6px 0 8px;
}
body.portal-v3 .v3-mrs-leyenda { border-top: 1px solid #ece7f5; padding-top: 6px; margin-top: 10px; }
body.portal-v3 .v3-mrs-sev-it { color: #6b6580; font-size: .82rem; }

/* ══════════════════════════ E5.a ══════════════════════════ */

/* ── E5.a · Receta en un paso (solo v3) ───────────────────────────────────
   Los botones clásicos de receta Y de orden se RETIRAN de la vista (el
   flujo v3 es previsualizar + enviar en un acto; la orden se sumó en E5.b).
   display:none con !important: son botones .btn (la trampa
   del [hidden] ya conocida). */
body.portal-v3 #rx-dl-receta,
body.portal-v3 #rx-enviar-receta,
body.portal-v3 #rx-dl-orden,
body.portal-v3 #rx-enviar-orden { display: none !important; }

/* FASE 79: el botón de vista previa de la columna se retiró; el de pestaña
   (#rx-pv-receta-full) lo SUSTITUYE, así que hereda exactamente su estilo — si
   se quedaba con el .btn--ghost genérico, el par de la receta perdía su color y
   dejaba de leerse como pareja frente al de la orden. */
body.portal-v3 #rx-pv-receta-full {
  background: #fff !important;
  border: 2px solid var(--v3-receta-txt) !important;
  color: var(--v3-receta-txt) !important;
}
body.portal-v3 #rx-un-paso {
  background: var(--v3-receta) !important;
  border: 2px solid var(--v3-receta-txt) !important;
  color: var(--v3-receta-txt) !important;
  font-weight: 700;
}
body.portal-v3 #rx-un-paso.v3-up-confirmando {
  outline: 3px solid var(--v3-receta-txt);
  outline-offset: 2px;
}
/* E5.b: el par de la ORDEN, con SU paleta (texto oscuro del mismo matiz:
   con blanco encima este rosa no pasa de 2:1). */
body.portal-v3 #rx-pv-orden-full {
  background: #fff !important;
  border: 2px solid var(--v3-orden-txt) !important;
  color: var(--v3-orden-txt) !important;
}
body.portal-v3 #rx-un-paso-orden {
  background: var(--v3-orden) !important;
  border: 2px solid var(--v3-orden-txt) !important;
  color: var(--v3-orden-txt) !important;
  font-weight: 700;
}
body.portal-v3 #rx-un-paso-orden.v3-up-confirmando {
  outline: 3px solid var(--v3-orden-txt);
  outline-offset: 2px;
}
body.portal-v3 .v3-up-estado {
  font-size: .85rem;
  margin-top: 8px;
  min-height: 1.2em;
  color: #2a2440;
  line-height: 1.45;
}
body.portal-v3 .v3-up-estado.err { color: #8a1f11; }
body.portal-v3 .v3-up-pdf {
  margin-top: 6px;
  font-size: .85rem;
  font-weight: 600;
  color: var(--v3-receta-txt);
  text-decoration: underline;
}
body.portal-v3 .v3-up-pdf--or { color: var(--v3-orden-txt); }
/* E5.c: la confirmación armada del certificado y del GES, con SU paleta. */
body.portal-v3 #cert-emitir.v3-up-confirmando {
  outline: 3px solid var(--v3-cert-txt);
  outline-offset: 2px;
}
body.portal-v3 #ges-emitir.v3-up-confirmando {
  outline: 3px solid var(--v3-ges-txt);
  outline-offset: 2px;
}

/* ── Legibilidad transversal (sin mover nada de sitio) ────────────────────
   Los botones chicos del portal actual bajan de 16 px reales; se corrige el
   mínimo tipográfico SIN cambiar la maqueta. */
body.portal-v3 .btn--sm { font-size: .88rem; min-height: 38px; }
body.portal-v3 button:focus-visible {
  outline: 3px solid #5b4fcf;
  outline-offset: 2px;
}

/* ── FICHA-A (29-jul) · la ficha del mockup aprobado ──────────────────────
   Cabecera de la paciente (identidad + píldora de antecedentes), solapas con
   presencia, grupos como tarjetas y el grupo privado inconfundible. Todo bajo
   body.portal-v3: sin la clase, inerte. */
body.portal-v3 .v3-fpac {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  background: #faf9ff;
  border: 1.5px solid #e6e0f5;
  border-radius: 14px;
  padding: 12px 16px;
  margin-bottom: 12px;
}
body.portal-v3 .v3-fpac-ava {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  /* O-3 (QA): el extremo claro del degradado dejaba el blanco en 3.25:1. */
  background: #4a3fb5;
  color: #fff;
  font-weight: 800;
  font-size: 1.15rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
body.portal-v3 .v3-fpac-idnt b {
  display: block;
  font-size: 1.15rem;
  line-height: 1.2;
}
body.portal-v3 .v3-fpac-idnt span {
  font-size: .85rem;
  color: #5a5470;
}
body.portal-v3 .v3-fpill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  padding: 6px 12px;
  font-size: .84rem;
  font-weight: 700;
  line-height: 1.25;
}
/* Recalculados por QA: ok 8.45 · ámbar 7.35 · rojo 8.04 · neutra 10.4 (AAA todos) */
body.portal-v3 .v3-fpill--ok    { background: #e6f7ef; color: #0c5132; }
body.portal-v3 .v3-fpill--ambar { background: #fff3d6; color: #6a4a00; }
body.portal-v3 .v3-fpill--rojo  { background: #fdecea; color: #8a1f11; }
body.portal-v3 .v3-fpill--neutra { background: #eef0f3; color: #333a45; }

/* Solapas de la ficha: los estilos vienen EN LÍNEA desde mfTabBtn/mfMostrarTab,
   por eso el !important — acotado a v3, no hay otra forma de ganarles. */
body.portal-v3 .mf-tab {
  font-size: .95rem !important;
  padding: 10px 18px !important;
  border-radius: 10px 10px 0 0;
}
body.portal-v3 .mf-tab.is-on {
  /* D-2 (QA): mfTabBtn emite background:none EN LÍNEA — sin !important esta
     regla no pinta nunca. color y border-bottom quedan al inline a propósito
     (mfMostrarTab los alterna). */
  background: #f2effe !important;
}

/* Grupos de la ficha como tarjetas (modal y sala) */
body.portal-v3 .fc-tramite {
  border: 1.5px solid #e6e0f5;
  border-radius: 12px;
  background: #fff;
  margin-bottom: 10px;
}
body.portal-v3 .fc-tramite > summary { padding: 10px 12px; }
body.portal-v3 .fc-tramite-tit { color: #4a3fb5; }

/* El grupo "Solo para ti", inconfundible: es la promesa de privacidad. */
body.portal-v3 .fc-g--privado {
  background: #f6f4fd;
  border-color: #cdc6f2;
}
body.portal-v3 .fc-g--privado > summary .fc-tramite-tit::before {
  content: "🔒 ";
}


/* ═══════════════════════ FASE 55 · SALA "PROPUESTA A" ═══════════════════════
   Spec de Josefa (29-jul-2026). TODO bajo body.portal-v3: sin la clase, esta
   hoja sigue siendo inerte y el portal clásico no cambia ni un píxel.

   Decisiones de accesibilidad que NO se negocian (H1/H2 de la spec):
    · Las 5 tarjetas van al 100 % de opacidad (la .72 de la maqueta reprobaba
      AA: 3,69–4,02). La activa se marca con anillo + elevación, no con opacidad.
    · Los tonos durazno/celeste de la maqueta se corrigieron a #8a5a00 (5,24) y
      #1f6ba8 (5,00); --tinta-3 (3,47) queda PROHIBIDO en las bajadas: para eso
      está --v3-meta (6,45).
    · Cada estado se dice con ícono + texto, nunca solo con color (WCAG 1.4.1).
   Las variables --sombra/--sombra-sm/--linea/--tinta llevan valor de respaldo:
   si el token no estuviera definido en la hoja base, la declaración entera se
   caería en silencio.                                                        */

body.portal-v3 {
  --v3-meta: #605a7d;
  --v3-lat-w: 236px;   /* F57: el lateral del mockup */
  /* 🔴 FASE 62e — el hueco del contenido SALE de la variable, no de un 252
     escrito a mano. Así plegar el lateral mueve TODO con un solo cambio:
     lateral, topbar, franja, contenido y pie. */
  --v3-lat-hueco: calc(var(--v3-lat-w) + 16px);
  /* 🔴 FASE 62a2 — alto reservado en el pie del lateral para que el FAB de Ayuda
     no tape texto. El FAB lo dibuja soporte-fab.js:133 (position:fixed,
     left:16px, bottom:16px) y NO es nuestro: si allá cambia el tamaño, acá se
     ajusta UN número y no hay que ir a buscar un 64 suelto en la hoja. */
  --v3-fab-hueco: 64px;
  --v3-topbar-h: 62px;
  --v3-pl-r: 18px;
  --v3-in-r: 12px;
}

/* Transiciones cortas y SOLO sobre propiedades baratas; nunca `all`. */
@media (prefers-reduced-motion: reduce) {
  body.portal-v3 * { transition: none !important; animation: none !important; }
}

/* 🔴 Trampa de la casa: una clase con display:flex/inline-flex le GANA al
   [hidden] del navegador. Todo nodo nuevo que se apaga por atributo se apaga
   también acá (además del style.display que pone el JS). */
body.portal-v3 #v3-topbar [hidden],
body.portal-v3 .v3-acc__marca[hidden],
body.portal-v3 .v3-pl__pill[hidden],
body.portal-v3 #v3-docpac-badge[hidden],
body.portal-v3 #v3-lat[hidden],
body.portal-v3 .v3-campana__n[hidden],
body.portal-v3 #v3-reloj[hidden],
body.portal-v3 #v3-nota[hidden] { display: none !important; }

/* ── §F57.1 · MENÚ LATERAL DEL MOCKUP (236px) ────────────────────────────
   Reemplaza al rail de 72px que Mauricio revocó. ⚠️ pro.css NO está en este
   workspace: los valores de color/estado son los de la F55, que Josefa calculó
   sobre ESTE mismo degradado y verificó en contraste (reposo #cfc9f5 = 7,87 ·
   activo #fff sobre el gradiente violeta = 5,51). El ancho, el tamaño de ícono
   (19px) y el cuerpo del rótulo (.94rem) son los del contrato.
   `fixed` en vez del `sticky` del mockup: el lateral NO entra al grid de la
   sala, así no toca el recálculo de alturas del modo jitsi. Visualmente es la
   misma columna a altura completa. */
body.portal-v3 #v3-lat {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  width: var(--v3-lat-w);
  background: linear-gradient(180deg, #332c66 0%, #1e1a3f 100%);
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px 12px;
  z-index: 40;
  overflow-y: auto;
}
body.portal-v3 .v3-lat__logo {
  border: 0;
  background: none;
  cursor: pointer;
  text-align: left;
  padding: 4px 8px 14px;
  font: 700 1.3rem/1.2 Poppins, sans-serif;
  color: #fff;
}
/* 🔴 FASE 63a — el selector viejo (`.v3-lat__logo span`) alcanzaba TAMBIÉN a
   .v3-lat__logo-tx, que es un <span>: el wordmark entero salía salmón y el
   "Nexo" blanco del mockup no existía. Ahora pinta solo el hijo directo. */
body.portal-v3 .v3-lat__logo-tx > span { color: #ff9d8a; }   /* 8,19 sobre #1e1a3f · 6,17 sobre #332c66 */
body.portal-v3 .v3-lat__logo { display: flex; align-items: center; gap: 0; }
body.portal-v3 .v3-lat__logo-tx { display: block; line-height: 1.1; }
/* Bajada del mockup (index.html:29 + portal.css:51). Mismo tono que el pie del
   lateral: 6,57 sobre #1e1a3f y 4,94 sobre #332c66 (el peor punto del
   degradado). No se inventa un gris nuevo para esto. */
body.portal-v3 .v3-lat__logo-tx small {
  display: block;
  font: 400 .7rem/1.3 var(--fuente, Inter, sans-serif);
  color: #a49ecf;
  margin-top: 2px;
}
body.portal-v3 .v3-lat__logo:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
body.portal-v3 .v3-lat__nav { display: flex; flex-direction: column; gap: 4px; }
body.portal-v3 .v3-lat-it {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 0;
  border-radius: 12px;
  background: none;
  color: #cfc9f5;                        /* 7,87 sobre el degradado */
  font: 600 .94rem/1.25 Inter, sans-serif;
  text-align: left;
  cursor: pointer;
}
body.portal-v3 .v3-lat-it__ic { display: inline-flex; flex: none; }
body.portal-v3 .v3-lat-it svg { width: 19px; height: 19px; }
body.portal-v3 .v3-lat-it:hover { background: rgba(255, 255, 255, .08); color: #fff; }
body.portal-v3 .v3-lat-it.is-on {
  background: linear-gradient(135deg, #5b4fcf, #6257d2);   /* 5,51 con #fff */
  color: #fff;
  box-shadow: 0 6px 16px rgba(91, 79, 207, .40);
  cursor: default;
}
body.portal-v3 .v3-lat-it:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
body.portal-v3 .v3-lat-sep {
  margin-top: 10px;
  border-top: 1px solid rgba(255, 255, 255, .14);
  padding-top: 14px;
  border-radius: 0 0 12px 12px;
}
/* Pie de conexión: DATO REAL (navigator.onLine + effectiveType), con el title
   que dice de quién es la estimación. */
body.portal-v3 .v3-lat__pie {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 12px 4px;
  border-top: 1px solid rgba(255, 255, 255, .14);
  color: #cfc9f5;
  font: 600 .8rem/1.3 Inter, sans-serif;
}
body.portal-v3 .v3-lat__punto { width: 9px; height: 9px; border-radius: 999px; flex: none; }
body.portal-v3 .v3-lat__punto.es-on { background: #6ee7a8; }
body.portal-v3 .v3-lat__punto.es-off { background: #ff8a7a; }
/* 🔴 FASE 62a — el pie a DOS líneas cortas, con el hueco del FAB reservado.
   Dos defectos en la misma caja de 236px:
     · "Conectado · Buena conexión" envolvía (el texto ahora vive en dos líneas
       cortas y la calidad se fue al title);
     · el FAB de Ayuda —#cd-soporte-fab de soporte-fab.js, position:fixed,
       left:16px, bottom:16px, ~56px de lado— se dibuja ENCIMA del lateral y
       tapaba la línea de abajo. soporte-fab.js NO se toca: el que cede es el
       pie, que reserva 64px y así NINGÚN texto queda bajo el FAB.
   Las dos líneas van nowrap: caben en 236 − 24 (padding) − 9 (punto) − 8 (gap).
   Colores del mockup (portal.css:55-56); contraste recalculado en
   verificar-fase62.py sobre los DOS extremos del degradado, no solo el oscuro. */
body.portal-v3 .v3-lat__pie-tx { min-width: 0; }
body.portal-v3 .v3-lat__pie b {
  display: block;
  color: #e9e6fa;
  font: 600 .85rem/1.3 Inter, sans-serif;
  white-space: nowrap;
}
body.portal-v3 .v3-lat__pie small {
  display: block;
  color: #a49ecf;
  font: 400 .76rem/1.3 Inter, sans-serif;
  white-space: nowrap;
}
body.portal-v3 #v3-lat .v3-lat__pie { padding-bottom: var(--v3-fab-hueco, 64px); }
@media (min-width: 980px) {
  /* El lateral no empuja: la sala se corre con padding. F58: el contenedor
     deja de ser el bloque centrado de 1320px (estilos.css:98) — el mockup
     (pro.css .contenido) ancla el contenido al lateral y llena hasta 1560px.
     Sin esto, en 1920 el contenido arrancaba ~550px del borde: la franja
     muerta que Mauricio marcó en verde. box-sizing global = border-box. */
  body.portal-v3 #pantalla-sesion .contenedor {
    padding-left: var(--v3-lat-hueco);
    padding-right: 28px;
    max-width: calc(var(--v3-lat-hueco) + 1560px);
    margin-inline: 0;
  }
  /* "Volver al panel" es redundante con el lateral; bajo 980 REAPARECE solo. */
  body.portal-v3 #pantalla-sesion #ses-volver { display: none; }
}
@media (max-width: 979px) {
  body.portal-v3 #v3-lat { display: none !important; }
}

/* ── §3 · Topbar de la sala ────────────────────────────────────────────── */
body.portal-v3 #v3-topbar {
  position: sticky;
  top: 73px;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  min-height: var(--v3-topbar-h);
  padding: 10px 0;
  background: rgba(247, 244, 255, .92);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--linea, #e6e0f5);
  margin-bottom: 14px;
}
body.portal-v3 #v3-topbar #ses-titulo {
  font: 700 1.25rem/1.3 Poppins, sans-serif;
  color: var(--tinta, #1e1a3f);
  margin: 0;
}
body.portal-v3 #v3-topbar #ses-sub {
  font-size: .85rem;
  color: var(--v3-meta);
  margin: 0;
  max-width: 38ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.portal-v3 .v3-topbar__gap { flex: 1 1 auto; }
body.portal-v3 #v3-ir-firma {
  min-height: 44px;
  padding: 10px 18px;
  border-radius: var(--v3-in-r);
  font-weight: 700;
  background: #0b7266 !important;              /* 5,81 con #fff */
  border: 2px solid #08544b !important;
  color: #fff !important;
}
body.portal-v3 #v3-ir-firma[disabled] {
  background: #ecebf5 !important;
  border-color: #d9d5ea !important;
  color: #6b6580 !important;
  cursor: not-allowed;
}

/* ── §F57.3 · La tarjeta de la paciente, en la columna derecha ────────────
   Revoca la banda sticky de la F55 (§1.2): Mauricio la quiere donde está en el
   mockup. Al entrar a la pila del panel DEJA de ser sticky (el mockup tampoco
   lo es) y su `top` se anula: si quedara, el sticky heredado la desplazaría
   dentro del scroller de 74vh. */
body.portal-v3 #pantalla-sesion .pac-ctx {
  position: static;
  top: auto;
  flex: none;
  margin: 0;
  border-radius: var(--v3-pl-r);
  box-shadow: var(--sombra, 0 10px 24px rgba(30, 26, 63, .14));
  border: 1px solid #e7e2f2;
  background: #fff;
}

/* ── §4 · Las 5 tarjetas de entrada ────────────────────────────────────── */
body.portal-v3 #v3-acciones {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
  gap: 12px;
  margin: 16px 0 18px;
}
body.portal-v3 .v3-acc {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
  min-height: 128px;
  padding: 14px;
  border-radius: var(--v3-pl-r);
  border: 1.5px solid;                  /* el borde oscuro es OBLIGATORIO */
  text-align: left;
  overflow: hidden;                     /* mockup .acc: el texto no sangra */
  cursor: pointer;
  box-shadow: var(--sombra-sm, 0 2px 8px rgba(30, 26, 63, .08));
  transition: transform .12s cubic-bezier(.2, 0, 0, 1),
              box-shadow .12s cubic-bezier(.2, 0, 0, 1);
}
body.portal-v3 .v3-acc:hover {
  transform: translateY(-2px);
  box-shadow: var(--sombra, 0 10px 24px rgba(30, 26, 63, .14));
}
body.portal-v3 .v3-acc:focus-visible { outline: 3px solid currentColor; outline-offset: 3px; }
body.portal-v3 .v3-acc__ic {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  background: #fff;
  display: grid;
  place-items: center;
}
body.portal-v3 .v3-acc__ic svg { width: 19px; height: 19px; }
body.portal-v3 .v3-acc b { font: 700 .95rem/1.25 Inter, sans-serif; }
body.portal-v3 .v3-acc .d { font-size: .79rem; line-height: 1.35; }
body.portal-v3 .v3-acc.is-on {
  box-shadow: 0 0 0 3px #fff, 0 0 0 6px currentColor,
              var(--sombra, 0 10px 24px rgba(30, 26, 63, .14));
}
body.portal-v3 .v3-acc[aria-disabled="true"] { opacity: .5; cursor: not-allowed; box-shadow: none; }
body.portal-v3 .v3-acc__marca {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: auto;
  border-radius: 999px;
  padding: 3px 9px;
  font: 700 .72rem/1.2 Inter, sans-serif;
}
body.portal-v3 .v3-acc__marca svg { width: 13px; height: 13px; }
body.portal-v3 .v3-acc__marca.es-ok       { background: #e6f7ef; color: #0c5132; }   /* 8,45 */
body.portal-v3 .v3-acc__marca.es-borrador { background: #fdecea; color: #8a1f11; }   /* 8,04 */
/* Relleno + texto de cada tarjeta: los 5 pares de la paleta de Mauricio
   (7,22–7,48 = AAA). Sin opacidad: la inactiva se lee igual de bien. */
body.portal-v3 .v3-acc--receta  { background: var(--v3-receta);  color: var(--v3-receta-txt);  border-color: var(--v3-receta-txt); }
body.portal-v3 .v3-acc--orden   { background: var(--v3-orden);   color: var(--v3-orden-txt);   border-color: var(--v3-orden-txt); }
body.portal-v3 .v3-acc--cert    { background: var(--v3-cert);    color: var(--v3-cert-txt);    border-color: var(--v3-cert-txt); }
body.portal-v3 .v3-acc--control { background: var(--v3-control); color: var(--v3-control-txt); border-color: var(--v3-control-txt); }
body.portal-v3 .v3-acc--ges     { background: var(--v3-ges);     color: var(--v3-ges-txt);     border-color: var(--v3-ges-txt); }
body.portal-v3 .v3-acc svg { color: inherit; }

/* ── §1.4 · Panel derecho: pila de desplegables ───────────────────────────
   Las pestañas se apagan por completo; sus 4 contenedores quedan vacíos en el
   DOM y activarSalaTab() los sigue toggleando sin efecto (contrato intacto). */
body.portal-v3 #pantalla-sesion .sala-tabs { display: none; }
body.portal-v3 .sesion-sala.modo-jitsi .sesion-panel {
  height: 74vh;                 /* el innegociable de medico.js */
  min-height: 440px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-right: 4px;
}
body.portal-v3 .sesion-sala.modo-jitsi .sala-tab-cont:not([hidden]) { overflow: visible; flex: none; }
body.portal-v3 .v3-pl {
  background: #fff;
  border-radius: var(--v3-pl-r);
  box-shadow: var(--sombra-sm, 0 2px 8px rgba(30, 26, 63, .08));
  overflow: hidden;
  flex: none;
}
body.portal-v3 .v3-pl > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  min-height: 56px;
}
body.portal-v3 .v3-pl > summary::-webkit-details-marker { display: none; }
body.portal-v3 .v3-pl > summary:focus-visible { outline: 3px solid #5b4fcf; outline-offset: -3px; }
body.portal-v3 .v3-pl__ic {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  flex: none;
}
body.portal-v3 .v3-pl__ic svg { width: 19px; height: 19px; }
body.portal-v3 .v3-pl__tit { font: 600 1.05rem/1.3 Poppins, sans-serif; color: var(--tinta, #1e1a3f); }
body.portal-v3 .v3-pl__txt {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
body.portal-v3 .v3-pl__sub { font-size: .79rem; color: var(--v3-meta); line-height: 1.35; }
body.portal-v3 .v3-pl__fl {
  margin-left: auto;
  color: var(--v3-meta);
  display: inline-flex;
  transform: rotate(90deg);
  transition: transform .12s cubic-bezier(.2, 0, 0, 1);
}
body.portal-v3 .v3-pl[open] .v3-pl__fl { transform: rotate(-90deg); }
body.portal-v3 .v3-pl__cuerpo { padding: 0 18px 18px; }
body.portal-v3 .v3-pl__ic--violeta { background: #ede9ff; color: #5b4fcf; }   /* 5,11 */
body.portal-v3 .v3-pl__ic--celeste { background: #e8f3fb; color: #1f6ba8; }   /* 5,00 */
body.portal-v3 .v3-pl__ic--ambar { background: #fbf0da; color: #8a5a00; }   /* 5,24 */
body.portal-v3 .v3-pl__ic--menta   { background: #e2f5f3; color: #0b7266; }   /* 5,14 */
body.portal-v3 .v3-pl__pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  padding: 3px 10px;
  font: 700 .72rem/1.2 Inter, sans-serif;
}
body.portal-v3 .v3-pl__pill svg { width: 13px; height: 13px; }
body.portal-v3 .v3-pl__pill.es-borrador { background: #fdecea; color: #8a1f11; }   /* 8,04 */
body.portal-v3 #v3-docpac-badge {
  display: inline-block;
  min-width: 20px;
  border-radius: 999px;
  padding: 2px 7px;
  background: #8a1f11;
  color: #fff;
  font: 700 .72rem/1.4 Inter, sans-serif;
  text-align: center;
}

/* ── §1.5 · El Sobre ──────────────────────────────────────────────────── */
body.portal-v3 #v3-sobre {
  background: #fff;
  border-radius: var(--v3-pl-r);
  box-shadow: var(--sombra-sm, 0 2px 8px rgba(30, 26, 63, .08));
  padding: 16px 18px;
  flex: none;
}
body.portal-v3 #v3-sobre .v3-sobre__tit {
  font: 600 1.05rem/1.3 Poppins, sans-serif;
  color: var(--tinta, #1e1a3f);
  margin: 0;
}
/* FASE 56 (G.1): los chips se OCULTAN. Siguen siendo el disparador —
   pintarReceta los recrea, refrescarChipsDocs los repinta y de ahí sale
   v3AccSync— pero lo que se ve es el render nuevo del Sobre, con folio,
   destinatario, descarga y reenvío por ítem. El nodo y su id NO se tocan. */
body.portal-v3 #v3-sobre #rx-chips { display: none; }
body.portal-v3 .v3-sobre-vacio {
  display: none;
  margin: 8px 0 0;
  font-size: .85rem;
  color: var(--v3-meta);
}
/* FASE 56: SE RETIRAN las dos reglas que decidían el estado vacío por
   selector (#rx-chips:empty + hermano adyacente / :first-child). Ahora el
   Sobre tiene ítems propios delante del vacío, así que el hermano adyacente
   ya no es #rx-chips y la regla habría dejado de coincidir EN SILENCIO.
   Lo decide v3SobreRender(), que es quien sabe si hubo envíos. */

/* ── §5.6 · Transcripción con el mismo chrome de card ─────────────────── */
body.portal-v3 #trx-box {
  border-radius: var(--v3-pl-r);
  box-shadow: var(--sombra-sm, 0 2px 8px rgba(30, 26, 63, .08));
  background: #fff;
  overflow: hidden;
  border: 0;
}
body.portal-v3 #trx-box > .pc-resumen { padding: 14px 18px; min-height: 56px; gap: 11px; }

/* ── §1.6 · Cajón GES/certificado: se suma #v3-acciones a lo que se oculta.
   (#receta-emision ya no vive bajo el video, pero la regla se conserva
   completa y explícita: es más barato leerla que deducirla.) ───────────── */
body.portal-v3 .v3-cajon-abierto .sesion-panel,
body.portal-v3 .v3-cajon-abierto #v3-acciones,
body.portal-v3 .v3-cajon-abierto #receta-emision { display: none !important; }


/* ══════════════════════════════════════════════════════════════════════════
   FASE 56 · EL ACORDEÓN DE DOCUMENTOS (contrato DISENO-fase56.md)
   Cinco secciones apiladas dentro de "Documentos de la consulta": pastilla de
   color + nombre + badge de estado + chevron, cuerpo propio y botón de envío.
   🔴 El color NUNCA carga el significado solo: cada fila lleva pastilla + nombre
   + estado EN TEXTO (WCAG 1.4.1). Los pares de badge son los mismos ya usados y
   verificados en esta hoja: #e6f7ef/#0c5132 (8,45) y #fdecea/#8a1f11 (8,04).
   ══════════════════════════════════════════════════════════════════════════ */

/* El chrome clásico de la emisión queda VIVO en el DOM —sus seis botones son el
   cableado real del certificado, del GES y del envío clásico— pero fuera de la
   vista: los campos que la médica usa están MUDADOS a las secciones. Las cuatro
   reglas por-botón de más arriba quedan redundantes e inocuas. */
body.portal-v3 #receta-emision { display: none !important; }
/* Los h4 de grupo ("Medicamentos (receta)" / "Órdenes de examen") repetirían la
   cabecera de su sección. Se ocultan por CSS: en pintarReceta NO se editan
   (son textos compartidos con el portal clásico). */
body.portal-v3 .v3-doc .receta-grupo > h4 { display: none; }
/* El título de la sección de control ya lo dice la cabecera; el SUB se conserva
   (explica que el correo a la paciente sale al finalizar la consulta). */
body.portal-v3 .v3-doc .pc-seccion__tit { display: none; }
/* El MRS pasa a tener desplegable propio: el bloque de dentro de "Ficha" se
   oculta, pero el NODO y sus ids siguen vivos (la guarda de mrsEnviarDo
   pregunta por #mrs-ficha-box). */
body.portal-v3 #mrs-ficha-box { display: none !important; }

body.portal-v3 #v3-acordeon { display: flex; flex-direction: column; gap: 8px; }
body.portal-v3 .v3-doc {
  border: 1px solid #e7e2f2;
  border-radius: var(--v3-in-r);
  background: #fff;
  overflow: hidden;
}
/* button{border:none} de estilos.css hacía mudo el border-color: se declara
   explícito (misma trampa m1 documentada arriba). */
body.portal-v3 .v3-doc__cab {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 52px;
  padding: 10px 12px;
  border: 0;
  background: #fbfaff;
  cursor: pointer;
  text-align: left;
  font: 600 .95rem/1.3 Inter, sans-serif;
  color: var(--tinta, #1e1a3f);
}
body.portal-v3 .v3-doc__cab:hover { background: #f3f0ff; }
body.portal-v3 .v3-doc__cab:focus-visible { outline: 3px solid #5b4fcf; outline-offset: -3px; }
/* Pastilla de 10×26 con el borde oscuro por dentro (los cinco rellenos dan
   1,02–2,38 contra el fondo: sin borde, la de GES desaparece). */
body.portal-v3 .v3-doc__pt { width: 10px; height: 26px; border-radius: 999px; flex: none; }
body.portal-v3 .v3-doc--receta  .v3-doc__pt { background: var(--v3-receta);  box-shadow: inset 0 0 0 1px var(--v3-receta-txt); }
body.portal-v3 .v3-doc--orden   .v3-doc__pt { background: var(--v3-orden);   box-shadow: inset 0 0 0 1px var(--v3-orden-txt); }
body.portal-v3 .v3-doc--cert    .v3-doc__pt { background: var(--v3-cert);    box-shadow: inset 0 0 0 1px var(--v3-cert-txt); }
body.portal-v3 .v3-doc--control .v3-doc__pt { background: var(--v3-control); box-shadow: inset 0 0 0 1px var(--v3-control-txt); }
body.portal-v3 .v3-doc--ges     .v3-doc__pt { background: var(--v3-ges);     box-shadow: inset 0 0 0 1px var(--v3-ges-txt); }
body.portal-v3 .v3-doc__nom { flex: 1 1 auto; min-width: 0; }
body.portal-v3 .v3-doc__est {
  align-items: center;
  gap: 5px;
  flex: none;
  border-radius: 999px;
  padding: 3px 9px;
  font: 700 .72rem/1.2 Inter, sans-serif;
}
body.portal-v3 .v3-doc__est svg { width: 13px; height: 13px; }
body.portal-v3 .v3-doc__est.es-ok       { background: #e6f7ef; color: #0c5132; }   /* 8,45 */
body.portal-v3 .v3-doc__est.es-borrador { background: #fdecea; color: #8a1f11; }   /* 8,04 */
body.portal-v3 .v3-doc__fl {
  color: var(--v3-meta);
  display: inline-flex;
  flex: none;
  transform: rotate(90deg);
  transition: transform .12s cubic-bezier(.2, 0, 0, 1);
}
body.portal-v3 .v3-doc.abierto .v3-doc__fl { transform: rotate(-90deg); }
body.portal-v3 .v3-doc__cuerpo { display: none; padding: 12px; border-top: 1px solid #efecf8; }
body.portal-v3 .v3-doc.abierto .v3-doc__cuerpo { display: block; }
/* La sección abierta se marca además con el color de su documento (redundancia
   con el chevron y el aria-expanded, nunca sola). */
body.portal-v3 .v3-doc--receta.abierto  { border-color: var(--v3-receta-txt); }
body.portal-v3 .v3-doc--orden.abierto   { border-color: var(--v3-orden-txt); }
body.portal-v3 .v3-doc--cert.abierto    { border-color: var(--v3-cert-txt); }
body.portal-v3 .v3-doc--control.abierto { border-color: var(--v3-control-txt); }
body.portal-v3 .v3-doc--ges.abierto     { border-color: var(--v3-ges-txt); }

/* Envío por sección: el par previsualizar/enviar, su microcopy y su aviso de
   autoguardado. Los botones se los pone v3UnPasoMontar (ya tienen su color). */
body.portal-v3 .v3-doc__envio {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid #efecf8;
}
body.portal-v3 .v3-doc__micro {
  flex: 1 1 100%;
  margin: 0;
  font-size: .79rem;
  line-height: 1.4;
  color: var(--v3-meta);
}
body.portal-v3 .v3-doc__ag {
  margin-top: 8px;
  font-size: .74rem;
  color: var(--v3-meta);
  min-height: 1.1em;
}
/* Lanzadores del cajón ancho (certificado / GES). */
body.portal-v3 .v3-doc__lanz {
  margin: 0 0 10px;
  font-size: .85rem;
  line-height: 1.45;
  color: var(--tinta-1, #2a2440);
}
body.portal-v3 .v3-doc__lanznom { font-weight: 700; }
body.portal-v3 .v3-doc--cert .v3-doc__lanzbtn {
  background: var(--v3-cert) !important;
  border: 2px solid var(--v3-cert-txt) !important;
  color: var(--v3-cert-txt) !important;
  font-weight: 700;
}
body.portal-v3 .v3-doc--ges .v3-doc__lanzbtn {
  background: var(--v3-ges) !important;
  border: 2px solid var(--v3-ges-txt) !important;
  color: var(--v3-ges-txt) !important;
  font-weight: 700;
}
/* Control sin consulta en curso: la fila SIGUE estando (las cinco están
   siempre) y dice por qué no hay calendario. Se muestra por el hermano
   [hidden] de #pc-seccion, que es ESTADO y no se pisa desde acá. */
body.portal-v3 .v3-doc__nodisp {
  display: none;
  margin: 0;
  font-size: .85rem;
  color: var(--v3-meta);
}
body.portal-v3 #pc-seccion[hidden] ~ .v3-doc__nodisp { display: block; }
/* Pie del acordeón: la línea de estado clásica (#rx-estado) mudada. */
body.portal-v3 #rx-estado-host { margin-top: 10px; }

/* Píldora verde del summary de Documentos: mismo par que el badge .es-ok.
   SOLO se pinta con 0 borradores y al menos 1 enviado (lo decide el JS). */
body.portal-v3 .v3-pl__pill.es-ok { background: #e6f7ef; color: #0c5132; }   /* 8,45 */
body.portal-v3 .v3-mrs-pill { background: #ede9ff; color: #3f2fa8; }          /* 7,04 */

/* Chequeo del topbar: dice lo que falta enviar. */
body.portal-v3 #v3-chk {
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  padding: 4px 10px;
  font: 700 .74rem/1.2 Inter, sans-serif;
  max-width: 34ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.portal-v3 #v3-chk svg { width: 13px; height: 13px; flex: none; }
body.portal-v3 #v3-chk.es-viva { background: #fdecea; color: #8a1f11; }   /* 8,04 */
body.portal-v3 #v3-chk.es-ok   { background: #e6f7ef; color: #0c5132; }   /* 8,45 */

/* ── El Sobre fiel (G): un ítem por documento que YA salió ───────────────── */
body.portal-v3 #v3-sobre .v3-sobre__sub {
  margin: 4px 0 0;
  font-size: .79rem;
  line-height: 1.4;
  color: var(--v3-meta);
}
body.portal-v3 #v3-sobre-items { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
body.portal-v3 .v3-sob-it {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid #e7e2f2;
  border-radius: var(--v3-in-r);
  background: #fbfaff;
}
body.portal-v3 .v3-sob-nom { font: 700 .88rem/1.25 Inter, sans-serif; color: var(--tinta, #1e1a3f); }
body.portal-v3 .v3-sob-meta { flex: 1 1 100%; font-size: .78rem; color: var(--v3-meta); }
body.portal-v3 .v3-sob-est {
  border-radius: 999px;
  padding: 2px 9px;
  background: #e6f7ef;
  color: #0c5132;                                   /* 8,45 */
  font: 700 .72rem/1.3 Inter, sans-serif;
}
body.portal-v3 .v3-sob-pdf {
  font-size: .8rem;
  font-weight: 600;
  color: #0b7266;                                   /* 5,14 sobre #fbfaff */
  text-decoration: underline;
}
/* Acabado del Reenviar (condiciones de Josefa):
   · (d) el botón reserva de una vez el ancho del rótulo LARGO ("Confirmar el
     reenvío"): al armarse no crece y la fila no se mueve. 186px es el ancho del
     rótulo largo a .88rem Inter más el relleno — ⚠️ medir en navegador y ajustar
     ESTE número si sobra o falta (es el único punto de ajuste).
   · (e) 44px de alto real: .btn--sm de v3 los deja en 38 y esto es un objetivo
     táctil que dispara un correo con datos clínicos. */
body.portal-v3 .v3-sob-re {
  margin-left: auto;
  min-width: 186px;
  min-height: 44px;
  justify-content: center;
  text-align: center;
}
/* (e) El armado se marca con el par VIVO de advertencia que ya usa esta hoja
   (#fdecea/#8a1f11 = 8,04), NO con `outline`: el outline es del FOCO, y esta
   regla le ganaba en especificidad (0,3,0 contra el 0,2,1 de
   `button:focus-visible`) — mientras el botón estaba armado, el anillo de foco
   desaparecía. Separando propiedades, los dos estados se ven a la vez. */
body.portal-v3 .v3-sob-re.v3-up-confirmando {
  background: #fdecea !important;
  color: #8a1f11 !important;
  border: 2px solid #8a1f11 !important;
  font-weight: 700;
}
body.portal-v3 .v3-sob-pend {
  margin: 10px 0 0;
  font-size: .82rem;
  line-height: 1.45;
  color: #8a1f11;                                   /* 8,04 sobre blanco */
}


/* ══════════════════════════════════════════════════════════════════════════
   FASE 57 · LA CARCASA IGUAL AL MOCKUP (contrato CONTRATO-fase57.md)
   ══════════════════════════════════════════════════════════════════════════ */

/* ── §F57.2 · UNA SOLA BARRA. El header global se oculta SOLO en la sala v3
   (clase v3-en-sala, que sincroniza v3LatSync desde el observer de pantallas).
   Fuera de la sala vuelve intacto: es la barra de todo el resto del portal. */
body.portal-v3.v3-en-sala header.topbar { display: none; }
body.portal-v3.v3-en-sala #v3-topbar { top: 0; }
/* El cronómetro real vive en el topbar pero se LEE en la pill sobre el video:
   acá se oculta (su [hidden] es ESTADO de iniciarCron/detenerCron y no se toca
   desde el CSS ni desde el JS nuevo). */
body.portal-v3 #v3-topbar #ses-timer { display: none !important; }
/* El h2/p reales del título quedan mudados dentro del topbar, con sus ids
   vivos: el chrome del mockup ("Telemedicina" + la bajada) los reemplaza. */
body.portal-v3 #v3-topbar #ses-titulo,
body.portal-v3 #v3-topbar #ses-sub { display: none !important; }
body.portal-v3 .v3-top__txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
body.portal-v3 .v3-top__tit {
  margin: 0;
  font: 700 1.25rem/1.25 Poppins, sans-serif;
  color: var(--tinta, #1e1a3f);
}
body.portal-v3 .v3-top__sub {
  margin: 0;
  font-size: .82rem;
  color: var(--v3-meta);
  max-width: 34ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Buscador de pacientes (real: vuelca en #mp-q y delega la navegación). */
body.portal-v3 .v3-buscar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 12px;
  border: 1px solid var(--linea, #e6e0f5);
  border-radius: 999px;
  background: #fff;
}
body.portal-v3 #v3-buscar-q {
  border: 0;
  outline: none;
  background: none;
  min-width: 168px;
  min-height: 36px;
  font: 400 .88rem/1.3 Inter, sans-serif;
  color: var(--tinta, #1e1a3f);
}
body.portal-v3 .v3-buscar__go {
  border: 0;
  border-radius: 999px;
  background: #ede9ff;
  color: #5b4fcf;                      /* 5,11 */
  min-width: 34px;
  min-height: 34px;
  display: grid;
  place-items: center;
  cursor: pointer;
}
body.portal-v3 .v3-buscar__go svg { width: 15px; height: 15px; }
body.portal-v3 .v3-buscar:focus-within { box-shadow: 0 0 0 3px rgba(91, 79, 207, .25); }
/* Campana: el contador SOLO existe si hay novedades reales cargadas. */
body.portal-v3 .v3-campana {
  position: relative;
  border: 1px solid var(--linea, #e6e0f5);
  border-radius: 999px;
  background: #fff;
  color: var(--tinta, #1e1a3f);
  min-width: 44px;
  min-height: 44px;
  display: grid;
  place-items: center;
  cursor: pointer;
}
body.portal-v3 .v3-campana svg { width: 19px; height: 19px; }
body.portal-v3 .v3-campana__n {
  position: absolute;
  top: -3px;
  right: -3px;
  min-width: 19px;
  border-radius: 999px;
  padding: 1px 5px;
  background: #8a1f11;                 /* 8,04 con #fff */
  color: #fff;
  font: 700 .68rem/1.5 Inter, sans-serif;
  text-align: center;
}
/* Identidad de la profesional (mismos datos del header global). */
body.portal-v3 .v3-ident { display: flex; align-items: center; gap: 9px; }
body.portal-v3 .v3-ident__txt { display: flex; flex-direction: column; text-align: right; }
body.portal-v3 .v3-ident__txt b { font: 700 .85rem/1.2 Inter, sans-serif; color: var(--tinta, #1e1a3f); }
body.portal-v3 .v3-ident__txt span { font-size: .74rem; color: var(--v3-meta); }
body.portal-v3 .v3-ident__av {
  width: 38px;
  height: 38px;
  border-radius: 999px;
  background: linear-gradient(135deg, #5b4fcf, #6257d2);
  color: #fff;                         /* 5,51 */
  display: grid;
  place-items: center;
  font: 700 .82rem/1 Inter, sans-serif;
  flex: none;
}

/* ── §F57.4 · La nota clínica bajo el video ──────────────────────────────── */
body.portal-v3 #v3-nota {
  background: #fff;
  border: 1px solid #e7e2f2;
  border-radius: var(--v3-pl-r);
  box-shadow: var(--sombra-sm, 0 2px 8px rgba(30, 26, 63, .08));
  padding: 14px 16px;
  margin: 0 0 14px;
}
body.portal-v3 .v3-nota__cab { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
body.portal-v3 .v3-nota__tit {
  margin: 0;
  font: 600 1.05rem/1.3 Poppins, sans-serif;
  color: var(--tinta, #1e1a3f);
}
body.portal-v3 .v3-nota__pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  padding: 3px 10px;
  background: #ede9ff;
  color: #3f2fa8;                      /* 7,04 */
  font: 700 .72rem/1.2 Inter, sans-serif;
}
body.portal-v3 .v3-nota__pill svg { width: 13px; height: 13px; }
body.portal-v3 .v3-nota__priv {
  margin: 8px 0 0;
  font-size: .79rem;
  line-height: 1.45;
  color: var(--v3-meta);
}
body.portal-v3 .v3-nota__cuerpo { margin-top: 10px; }
/* El h5 interno repetiría el título de la card (el <label for="fc-nota"> sigue
   en el DOM: la asociación del campo con su rótulo NO se pierde). */
body.portal-v3 #v3-nota .fc-nota-item > h5 { display: none; }

/* ── §F57.5 · Reloj sobre el video ──────────────────────────────────────── */
body.portal-v3 #ses-escenario { position: relative; }
body.portal-v3 #v3-reloj {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 3;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  padding: 6px 12px;
  background: rgba(11, 114, 102, .94);
  color: #fff;                         /* 5,81 sobre #0b7266 */
  font: 700 .78rem/1.2 Inter, sans-serif;
  pointer-events: none;
}
body.portal-v3 #v3-reloj.es-over { background: rgba(138, 31, 17, .94); }   /* 8,04 */

/* ── §F58 · Las tarjetas con la ESCALERA del mockup (sala-a.html:46-48):
   5 por fila recién desde 1501px de pantalla; entre 761 y 1500, 3 por fila;
   bajo 760, 2. La F57 forzaba 5 desde 1200px: con la malla 50/50 de entonces
   cada tarjeta caía a ~92px y el texto sangraba (el reclamo de Mauricio). */
/* F58c: el corte de 5→3 NO es el 1500 del mockup sino una GARANTÍA de ancho:
   5 por fila solo cuando cada tarjeta alcanza ≥142px (la palabra más larga,
   "Notificación", necesita ~131px de caja). A 1501-1659 el propio mockup
   recorta — su breakpoint se aprobó en pantalla más ancha. En 1536 (1920 con
   escalado 125%, la pantalla real de Mauricio) quedan 3 por fila de ~222px. */
@media (min-width: 1660px) {
  body.portal-v3 #v3-acciones { grid-template-columns: repeat(5, 1fr); }
}
/* F58d · pedido de Mauricio: en su pantalla (~1536px CSS) las 5 van IGUAL en
   una fila — tarjeta COMPACTA para que el texto quepa entero. Funciona desde
   1460px (caja ≥98px; "Notificación" compacta ≈93px en la fuente de reserva).
   Bajo 1460 la garantía manda: 3 por fila. */
@media (min-width: 1460px) and (max-width: 1659px) {
  body.portal-v3 #v3-acciones { grid-template-columns: repeat(5, 1fr); gap: 10px; }
  body.portal-v3 .v3-acc { padding: 12px 10px; }
  body.portal-v3 .v3-acc b { font-size: .88rem; }
  body.portal-v3 .v3-acc .d { font-size: .74rem; }
  body.portal-v3 .v3-acc__ic { width: 30px; height: 30px; }
}
@media (min-width: 761px) and (max-width: 1459px) {
  body.portal-v3 #v3-acciones { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 760px) {
  body.portal-v3 #v3-acciones { grid-template-columns: 1fr 1fr; }
}
/* (xi) QA F57: los items de grid traen min-width:auto y no encogen bajo su
   min-content; ahora min-width:0 SIEMPRE (no solo en la fila de 5). */
body.portal-v3 #v3-acciones .v3-acc { min-width: 0; }
/* F58e · cinturón: si algún ancho futuro vuelve a quedar corto, la palabra se
   parte y envuelve — nunca un texto recortado por el overflow:hidden. QA F58d:
   `break-word+hyphens:auto` guionaba descripciones en anchos SANOS (el flex
   dimensiona por min-content y quien partía era hyphens); `anywhere` reduce el
   min-content y solo parte cuando de verdad no cabe. */
body.portal-v3 .v3-acc b,
body.portal-v3 .v3-acc .d { overflow-wrap: anywhere; }

/* ══════════════════════════════════════════════════════════════════════════════
   §F60 · PORTAL CONSOLIDADO — piel del shell global y de las cuatro vistas
   (Inicio · Documentos · Estadísticas · Ajustes), fiel al mockup APROBADO
   docs/mockup-portal-consolidado/portal.css.

   TODO cuelga de body.portal-v3: sin esa clase esta sección es inerte y el
   portal clásico (Margot) no cambia ni un píxel.

   🔴 CLASES NUEVAS CON PREFIJO v3d- A PROPÓSITO. El mockup usa .card/.kpi/
   .kpis/.chip/.pill/.tabla, y varios de esos nombres YA EXISTEN en estilos.css
   con otro significado (.kpi y .kpis son los del panel clásico, .pill vive en la
   sala). Copiar los nombres habría hecho que la hoja base pintara encima de la
   nueva y viceversa. Lo que se copia del mockup son los VALORES, no los nombres.

   CONTRASTES CALCULADOS (no de memoria; fórmula WCAG 2.1, sobre los tokens
   reales de estilos.css: blanco #fff, página #f7f4ff, violeta-claro #ede9ff,
   menta-claro #e2f5f3, durazno-claro #fbf0da, celeste-claro #e8f3fb):
     · texto secundario #605a7d ....... 6,45 sobre #fff · 5,94 sobre #f7f4ff
     · delta al alza    #0a6b52 ....... 6,49 sobre #fff
     · delta a la baja  #b3261e ....... 6,54 sobre #fff
     · píldora ok       #0a6b52/#e2f5f3 5,74
     · píldora aviso    #6b5200/#fbf0da 6,56
     · píldora info     #0b5e96/#e8f3fb 6,10
     · píldora marca    #5b4fcf/#ede9ff 5,11
     · chip activo      #fff/#5b4fcf ... 6,06
     · lateral, bajada  #bdb7e6/#332c66 6,54
   🔴 --tinta-3 (#8a86a8) = 3,47 sobre blanco: PROHIBIDO como texto. Donde el
   mockup lo usa, acá va #605a7d (misma jerarquía visual, contraste real).
   ══════════════════════════════════════════════════════════════════════════════ */

body.portal-v3 {
  --v3d-meta: #605a7d;
  --v3d-verde: #0a6b52;
  --v3d-rojo: #b3261e;
  --v3d-ambar: #6b5200;
  --v3d-azul: #0b5e96;
  --v3d-top-h: 74px;
}

/* Trampa de la casa (misma de la F57): una clase con display:flex le GANA al
   [hidden] del navegador. Todo nodo nuevo que se apaga por atributo se apaga
   también acá. */
body.portal-v3 #v3d-top[hidden],
body.portal-v3 .v3d-campana__n[hidden],
body.portal-v3 .v3d-menu[hidden],
body.portal-v3 .v3-lat-it[hidden] { display: none !important; }

/* ── El shell manda: una sola barra ────────────────────────────────────────
   El header clásico se oculta en TODO el portal v3 (no solo en la sala, como
   hacía la F57). Sus ids siguen vivos: pintarIdentidadTopbar() sigue
   escribiendo en #topbar-rol/#topbar-av y #marca-inicio sigue clickeable por
   delegación. */
body.portal-v3.v3-con-shell header.topbar { display: none; }

/* El contenido se corre para dejarle su columna al lateral fijo, y baja para
   dejar pasar la topbar fija. Mismos 252px de la F58 (236 del lateral + 16). */
@media (min-width: 980px) {
  body.portal-v3.v3-con-shell #pantalla-dashboard .contenedor,
  body.portal-v3.v3-con-shell #pantalla-agenda .contenedor,
  body.portal-v3.v3-con-shell #pantalla-mis-pacientes .contenedor,
  body.portal-v3.v3-con-shell #pantalla-mensajes .contenedor,
  body.portal-v3.v3-con-shell #pantalla-hoja-vida .contenedor,
  body.portal-v3.v3-con-shell #pantalla-seguridad .contenedor,
  body.portal-v3.v3-con-shell #pantalla-certificado .contenedor,
  body.portal-v3.v3-con-shell #pantalla-ges .contenedor,
  body.portal-v3.v3-con-shell #pantalla-v3d-docs .contenedor,
  body.portal-v3.v3-con-shell #pantalla-v3d-est .contenedor,
  /* 🔴 FASE 62a — FALTABA. Historia clínica es una pantalla v3d como las otras
     tres (v3dPantallasMontar las crea en el MISMO array, con la misma
     .seccion.contenedor), y sin su entrada acá quedaba centrada bajo el lateral
     fijo: el bug de layout que vio Mauricio. El gate de verificar-fase62.py
     recorre ese array del JS y exige una entrada por pantalla, así que una
     quinta pantalla futura no puede nacer sin pasar por esta lista. */
  body.portal-v3.v3-con-shell #pantalla-v3d-historia .contenedor,
  /* 🔴 FASE 62d-2 — la pantalla nueva ENTRA acá o queda bajo el lateral fijo.
     El gate de layout recorre el array de v3dPantallasMontar y exige una
     entrada por pantalla: por eso esta línea no se puede olvidar. */
  body.portal-v3.v3-con-shell #pantalla-v3d-pacientes .contenedor,
  body.portal-v3.v3-con-shell #pantalla-v3d-mensajes .contenedor,
  body.portal-v3.v3-con-shell #pantalla-v3d-agenda .contenedor,
  /* 🔴 FASE 63c — la ficha ENTRA acá o queda bajo el lateral fijo. Es la
     tercera vez que una pantalla nueva llega sin su entrada (62a, 62d-2 y
     esta): el gate de layout es lo único que lo impide, y solo sirve si
     nadie lo silencia. */
  body.portal-v3.v3-con-shell #pantalla-v3d-ficha .contenedor,
  body.portal-v3.v3-con-shell #pantalla-v3d-ajustes .contenedor {
    padding-left: var(--v3-lat-hueco);
    padding-right: 28px;
    max-width: calc(var(--v3-lat-hueco) + 1560px);
    margin-inline: 0;
  }
  /* 🔴 FASE 62a2 — EL PIE DEL PORTAL. Defecto preexistente (no lo trajo el
     shell): el footer vive FUERA de <main> y de todo #pantalla-*, así que
     ninguna entrada de la lista blanca lo alcanzaba y quedaba centrado en los
     1320px de .contenedor, pisado por el lateral fijo.
     🔴 SELECTOR COMPUESTO, no descendiente: el nodo real es
     <footer class="pie contenedor"> (medico.html.base:556) — las dos clases en
     el MISMO elemento. Un ".pie .contenedor" matchea 0 nodos y el arreglo
     quedaría mudo (que es justo cómo se ve un CSS que no aplica: igual que
     antes). Las 4 propiedades porque .contenedor (estilos.css:98) trae
     max-width:1320 + margin auto + padding-inline clamp: tocar solo el padding
     dejaba el ancho corto.
     🔴 DECISIÓN DECLARADA: la regla NO lleva :not(.v3-en-sala). Hoy no existe
     ninguna regla que oculte el footer en modo sala (0 coincidencias de .pie ni
     footer en esta hoja), así que en la sala el pie SE VE y el lateral está
     presente: correrlo es lo coherente. Si algún día se decide esconderlo en la
     sala, eso se hace con una regla de display, no mutilando esta. */
  body.portal-v3.v3-con-shell footer.pie.contenedor {
    padding-left: var(--v3-lat-hueco);
    padding-right: 28px;
    max-width: calc(var(--v3-lat-hueco) + 1560px);
    margin-inline: 0;
  }
  body.portal-v3.v3-con-shell:not(.v3-en-sala) main { padding-top: var(--v3d-top-h); }
  /* 🔴 FASE 62a2 — EL PIE, PEGADO ABAJO. Lo marcó Mauricio en su pantallazo: en
     una vista corta (Historia clínica con 2 pacientes) el footer quedaba en
     y≈434 con ~500px muertos debajo.
     EL CÁLCULO, medido y no supuesto:
       · box-sizing es border-box global (estilos.css.prod:78), así que el
         padding-top de la línea de arriba va DENTRO de este min-height: NO hay
         que descontar --v3d-top-h (además la topbar es `fixed`, no ocupa flujo).
       · el pie mide ~134px: 60 de padding (.pie 30px arriba y abajo,
         estilos.css.prod:707) + dos líneas de ~33px (los <a> son inline-block
         con padding 6px, estilos.css.prod:712) + 8 de margen entre ellas.
       · se reservan 140: sobrar deja el pie unos pocos px sobre el borde (no se
         nota); FALTAR crea scroll en TODA vista corta. El error se elige hacia
         el lado que no molesta.
     🔴 Scoped a :not(.v3-en-sala) y dentro del @media ≥980: la sala calcula sus
     alturas con su propio grid (modo jitsi) y un piso acá le agregaría scroll;
     bajo 980 el lateral se oculta y el layout es otro. */
  body.portal-v3.v3-con-shell:not(.v3-en-sala) main { min-height: calc(100vh - 140px); }
}

/* ── Topbar global ────────────────────────────────────────────────────────
   `fixed` y no `sticky`: cada pantalla es una <section> distinta y una barra
   sticky tendría que existir dentro de cada una. Fija, existe una sola vez.
   En la SALA no se muestra: ahí manda #v3-topbar (F57), que además lleva el
   cronómetro, la transcripción y "Firmar y cerrar". Nunca hay dos barras. */
body.portal-v3 #v3d-top {
  position: fixed;
  top: 0;
  left: var(--v3-lat-w);
  right: 0;
  z-index: 39;
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  min-height: var(--v3d-top-h);
  padding: 10px 28px;
  background: rgba(247, 244, 255, .93);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--linea, rgba(91, 79, 207, .14));
}
body.portal-v3 .v3d-top__saludo { min-width: 0; }
body.portal-v3 #v3d-top-tit {
  margin: 0;
  font: 600 1.28rem/1.2 var(--fuente-titulo, "Poppins", sans-serif);
  color: var(--tinta, #1e1a3f);
  display: flex;
  align-items: center;
  gap: 9px;
}
body.portal-v3 .v3d-mano { font-size: 1.1rem; }
body.portal-v3 .v3d-top__sub { margin: 2px 0 0; font-size: .86rem; color: var(--v3d-meta); }

body.portal-v3 .v3d-buscar {
  position: relative;
  flex: 1;
  min-width: 220px;
  max-width: 470px;
  margin: 0 auto;
  display: flex;
  align-items: center;
}
body.portal-v3 .v3d-buscar input {
  width: 100%;
  font: inherit;
  font-size: .92rem;
  /* Derecha: 78 (kbd) + su ancho ≈ 52 ⇒ el texto nunca pasa por debajo. */
  padding: 12px 136px 12px 44px;
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  border-radius: 14px;
  background: #fff;
  color: var(--tinta, #1e1a3f);
}
@media (max-width: 1180px) {
  body.portal-v3 .v3d-buscar input { padding-right: 74px; }   /* sin kbd */
}
body.portal-v3 .v3d-buscar input::placeholder { color: var(--v3d-meta); }
body.portal-v3 .v3d-buscar__lupa {
  position: absolute;
  left: 15px;
  color: var(--v3d-meta);
  display: flex;
  pointer-events: none;
}
body.portal-v3 .v3d-buscar__lupa svg { width: 18px; height: 18px; }
/* El botón existe de verdad (Enter también funciona): se muestra como texto
   para que el control sea alcanzable con teclado y con lector de pantalla. */
body.portal-v3 .v3d-buscar__go {
  position: absolute;
  right: 6px;
  border: 0;
  background: none;
  font: inherit;
  font-size: .82rem;
  font-weight: 700;
  color: var(--violeta, #5b4fcf);
  cursor: pointer;
  padding: 8px 10px;
  border-radius: 9px;
}
body.portal-v3 .v3d-buscar__go:hover { background: var(--violeta-claro, #ede9ff); }

body.portal-v3 .v3d-top__der { display: flex; align-items: center; gap: 12px; margin-left: auto; }
body.portal-v3 .v3d-campana {
  position: relative;
  width: 42px;
  height: 42px;
  border-radius: 12px;
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  background: #fff;
  color: var(--v3d-meta);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
body.portal-v3 .v3d-campana:hover { border-color: #cdc6f2; color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-campana__n {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 19px;
  height: 19px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--rosa, #e8674a);
  color: #fff;
  font: 700 .7rem/19px var(--fuente, Inter, sans-serif);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
body.portal-v3 .v3d-quien-caja { position: relative; }
body.portal-v3 .v3d-quien {
  display: flex;
  align-items: center;
  gap: 10px;
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
  padding: 5px 10px 5px 5px;
  border-radius: 12px;
}
body.portal-v3 .v3d-quien:hover { background: #fff; }
body.portal-v3 .v3d-quien__txt { text-align: left; }
body.portal-v3 .v3d-quien__txt b { display: block; font-size: .92rem; color: var(--tinta, #1e1a3f); }
body.portal-v3 .v3d-quien__txt span { font-size: .78rem; color: var(--v3d-meta); }
body.portal-v3 .v3d-quien__fl { display: flex; color: var(--v3d-meta); }
body.portal-v3 .v3d-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  min-width: 210px;
  background: #fff;
  border-radius: var(--r-sm, 12px);
  box-shadow: var(--sombra, 0 16px 40px rgba(91, 79, 207, .16));
  padding: 6px;
  z-index: 46;
}
body.portal-v3 .v3d-menu button {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  font: inherit;
  font-size: .9rem;
  padding: 10px 12px;
  border-radius: 9px;
  cursor: pointer;
  color: var(--tinta, #1e1a3f);
}
body.portal-v3 .v3d-menu button:hover { background: var(--violeta-claro, #ede9ff); color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-menu button.es-peligro:hover { background: #fce9e4; color: var(--v3d-rojo); }

/* 🔴 FASE 63a2 — acá vivía .v3d-lat__sub, la bajada vieja del lateral. Se
   retiró con su nodo: la bajada de la marca es ahora la del mockup
   («Ginecología y telemedicina»), que vive dentro de .v3-lat__logo-tx. */

/* ── El panel clásico queda montado pero fuera de vista ────────────────────
   #dash-kpis, #dash-citas y #dash-notifs SIGUEN VIVOS: de ahí sale el
   cronómetro del hero (#hero-cd) y los botones reales a los que el panel v3
   delega con .click(). Solo se oculta.
   Bajo 980px el shell v3 no cabe (el lateral ya se oculta desde la F57): ahí
   REAPARECE el panel clásico entero y se esconde el nuevo. Degradación
   declarada: el portal consolidado es de escritorio. */
@media (min-width: 980px) {
  body.portal-v3 #pantalla-dashboard .dash { display: none; }
}
@media (max-width: 979px) {
  body.portal-v3 #v3d-inicio { display: none; }
  body.portal-v3 #v3d-top { display: none !important; }
}
body.portal-v3 .v3d-volver {
  border: 0;
  background: none;
  font: inherit;
  font-size: .9rem;
  font-weight: 600;
  color: var(--violeta, #5b4fcf);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 2px;
  margin-bottom: 10px;
}
body.portal-v3 .v3d-volver:hover { text-decoration: underline; }
@media (min-width: 980px) {
  /* Con el lateral a la vista, "Volver al panel" es redundante (igual que en la
     sala, F58). Bajo 980 REAPARECE solo: es la única salida sin lateral. */
  body.portal-v3.v3-con-shell .v3d-volver { display: none; }
}

/* ── Piezas comunes ──────────────────────────────────────────────────────── */
body.portal-v3 .v3d-card {
  background: #fff;
  border-radius: var(--r, 18px);
  box-shadow: var(--sombra-sm, 0 6px 18px rgba(91, 79, 207, .10));
  padding: 20px 22px;
}
body.portal-v3 .v3d-card--hondo { box-shadow: var(--sombra, 0 16px 40px rgba(91, 79, 207, .16)); }
body.portal-v3 .v3d-card__cab { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
body.portal-v3 .v3d-card__cab h2 {
  margin: 0;
  font: 600 1.05rem/1.25 var(--fuente-titulo, "Poppins", sans-serif);
  color: var(--tinta, #1e1a3f);
}
body.portal-v3 .v3d-card__cab .v3d-lnk,
body.portal-v3 .v3d-card__cab .v3d-sel,
body.portal-v3 .v3d-card__cab .v3d-contador { margin-left: auto; }
body.portal-v3 .v3d-sub-h {
  margin: 16px 0 8px;
  font: 600 .92rem/1.3 var(--fuente, Inter, sans-serif);
  color: var(--tinta, #1e1a3f);
}
body.portal-v3 .v3d-meta { font-size: .85rem; color: var(--v3d-meta); margin: 4px 0 0; }
body.portal-v3 .v3d-lnk {
  border: 0;
  background: none;
  font: inherit;
  font-size: .86rem;
  font-weight: 600;
  color: var(--violeta, #5b4fcf);
  cursor: pointer;
  padding: 4px 2px;
}
body.portal-v3 .v3d-lnk:hover { text-decoration: underline; }
body.portal-v3 .v3d-pie-card {
  text-align: center;
  border-top: 1px solid var(--linea, rgba(91, 79, 207, .14));
  margin-top: 10px;
  padding-top: 12px;
}
body.portal-v3 .v3d-contador {
  background: #f2f0fb;
  color: var(--v3d-meta);
  border-radius: 999px;
  font: 700 .8rem/1 var(--fuente, Inter, sans-serif);
  padding: 6px 11px;
}
body.portal-v3 .v3d-ic { display: inline-flex; }
body.portal-v3 .v3d-ic svg { width: 100%; height: 100%; }
body.portal-v3 .v3d-chipic {
  width: 38px;
  height: 38px;
  border-radius: 11px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
}
body.portal-v3 .v3d-chipic svg { width: 18px; height: 18px; }
body.portal-v3 .v3d-chipic--violeta { background: var(--violeta-claro, #ede9ff); color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-chipic--celeste { background: var(--celeste-claro, #e8f3fb); color: var(--v3d-azul); }
body.portal-v3 .v3d-chipic--menta { background: var(--menta-claro, #e2f5f3); color: var(--v3d-verde); }
body.portal-v3 .v3d-chipic--ambar { background: var(--durazno-claro, #fbf0da); color: var(--v3d-ambar); }
body.portal-v3 .v3d-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  font: 600 .8rem/1.2 var(--fuente, Inter, sans-serif);
  padding: 6px 12px;
}
body.portal-v3 .v3d-pill--ok { background: var(--menta-claro, #e2f5f3); color: var(--v3d-verde); }
body.portal-v3 .v3d-pill--aviso { background: var(--durazno-claro, #fbf0da); color: var(--v3d-ambar); }
body.portal-v3 .v3d-pill--info { background: var(--celeste-claro, #e8f3fb); color: var(--v3d-azul); }
body.portal-v3 .v3d-pill--marca { background: var(--violeta-claro, #ede9ff); color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-pill--neutro { background: #f2f0fb; color: var(--v3d-meta); }
body.portal-v3 .v3d-ava {
  border-radius: 50%;
  background: var(--grad-primario, linear-gradient(135deg, #5b4fcf, #7a6ee0));
  color: #fff;                        /* 6,06 sobre #5b4fcf */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--fuente-titulo, "Poppins", sans-serif);
  font-weight: 600;
  flex: none;
}
/* ══ FASE 63b · LAS CINCO FAMILIAS DEL AVATAR (mockup vistas.js:26-32) ══════
   🔴 LOS CINCO DEGRADADOS DEL MOCKUP FUERON AJUSTADOS. Con texto blanco, NINGUNO
   llegaba a 4,5 en su extremo claro —que es el punto que hay que declarar, la
   lección del avatar del perfil— y tres tampoco llegaban en el extremo oscuro:
       violeta  #7a6ee0 = 4,09   ·  #5b4fcf = 6,06
       celeste  #63aee6 = 2,41   ·  #3b8fd4 = 3,46
       menta    #0fa896 = 2,97   ·  #0b7266 = 5,81
       ambar    #e8a93c = 2,06   ·  #d4870a = 2,89
       terracota #f0956d = 2,28  ·  #e8674a = 3,25
   Precedente D-62b-7: se conserva la FAMILIA (el matiz exacto del mockup) y se
   baja la luminosidad hasta que el PEOR punto del degradado pase 4,5 con blanco.
   Los pares de abajo están recalculados en verificar-fase62.py, uno por uno y
   en sus DOS extremos — no se declara el promedio ni el color «principal».
   El tono es DECORATIVO: el nombre va escrito al lado en las nueve superficies.
   Las cinco familias se distinguen por MATIZ, no por luminosidad (todas quedan
   cerca de 4,8 a propósito), así que no se promete distinguibilidad para quien
   no percibe color: no hace falta, porque el color no informa nada. */
body.portal-v3 .v3d-ava--t0 { background: linear-gradient(135deg, #4c3cd5, #6e61dd); }  /* violeta · 7,21 → 4,75 */
body.portal-v3 .v3d-ava--t1 { background: linear-gradient(135deg, #1a66a0, #1e77ba); }  /* celeste · 6,08 → 4,77 */
body.portal-v3 .v3d-ava--t2 { background: linear-gradient(135deg, #0a6f64, #0c8174); }  /* menta   · 6,05 → 4,76 */
body.portal-v3 .v3d-ava--t3 { background: linear-gradient(135deg, #84590f, #9a6812); }  /* ambar   · 6,15 → 4,81 */
body.portal-v3 .v3d-ava--t4 { background: linear-gradient(135deg, #aa4011, #c64b14); }  /* terracota · 6,09 → 4,75 */
/* 🔴 El avatar PROPIO (#v3d-quien-av) NO entra a la paleta de las cinco
   familias: conserva el degradado de MARCA por orden.
   FASE 63c (ronda de Josefa) · SE SALDA LA DEUDA QUE LA 63b DEJÓ DECLARADA:
   su extremo claro (#7a6ee0) daba 4,09 con blanco —bajo 4,5—, medido y no
   ignorado. #6e61dd es el MISMO matiz (el extremo claro que ya usa
   .v3d-ava--t0) y cruza 4,5: la marca no se mueve y el texto blanco pasa.
   Recalculado en sus DOS extremos, nunca promediado:
       #5b4fcf = 6,06   ·   #6e61dd = 4,75
   🔴 NO se toca --grad-primario: esa variable la comparte TODO el portal y
   mover la marca entera no es lo que se aprobó.
   EL ANILLO dice "esta soy yo": en el header conviven la profesional y las
   pacientes, y todos son círculos con degradado. 2px del fondo del header
   (#f7f4ff, el de #v3d-top) + 1px índigo alrededor. Con box-shadow y NO con
   border: un borde cambiaría la caja y correría el avatar dentro de la
   topbar. */
body.portal-v3 #v3d-quien-av {
  background: linear-gradient(135deg, #5b4fcf, #6e61dd);
  box-shadow: 0 0 0 2px #f7f4ff, 0 0 0 3px var(--violeta, #5b4fcf);
}
body.portal-v3 .v3d-ava--40 { width: 40px; height: 40px; font-size: .86rem; }
body.portal-v3 .v3d-ava--72 { width: 72px; height: 72px; font-size: 1.5rem; }
body.portal-v3 .v3d-delta {
  margin-left: auto;
  font: 700 .76rem/1.2 var(--fuente, Inter, sans-serif);
  color: var(--v3d-verde);
  white-space: nowrap;
  flex: none;
}
body.portal-v3 .v3d-delta.es-baja { color: var(--v3d-rojo); }
body.portal-v3 .v3d-vacio { text-align: center; padding: 34px 18px; color: var(--v3d-meta); }
body.portal-v3 .v3d-vacio .v3d-chipic { width: 54px; height: 54px; border-radius: 16px; margin: 0 auto 12px; }
body.portal-v3 .v3d-vacio .v3d-chipic svg { width: 25px; height: 25px; }
body.portal-v3 .v3d-vacio b { display: block; font-size: 1rem; margin-bottom: 5px; color: var(--tinta, #1e1a3f); }
body.portal-v3 .v3d-vacio span { font-size: .9rem; }
body.portal-v3 .v3d-vacio__acc { margin-top: 14px; }
body.portal-v3 .v3d-sel {
  font: 600 .85rem/1.2 var(--fuente, Inter, sans-serif);
  color: var(--v3d-meta);
  background: #fff;
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  border-radius: 10px;
  padding: 9px 10px;
  cursor: pointer;
}

/* ── Malla del Inicio (mockup .inicio) ───────────────────────────────────── */
body.portal-v3 .v3d-inicio {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 390px;
  gap: 20px;
  align-items: start;
}
@media (max-width: 1280px) { body.portal-v3 .v3d-inicio { grid-template-columns: 1fr; } }
body.portal-v3 .v3d-col { display: flex; flex-direction: column; gap: 20px; min-width: 0; }

/* ── Hero ────────────────────────────────────────────────────────────────── */
body.portal-v3 .v3d-hero__cab { display: flex; align-items: center; gap: 12px; }
body.portal-v3 .v3d-hero__cab h2 {
  margin: 0;
  font: 600 1.02rem/1.25 var(--fuente-titulo, "Poppins", sans-serif);
  color: var(--tinta, #1e1a3f);
}
/* El color lo pone el JS espejando #hero-cd (verde/ámbar/rojo según el estado
   real de la cita). Acá solo el cuerpo. */
body.portal-v3 .v3d-hero__cd { font: 700 .92rem/1.3 var(--fuente, Inter, sans-serif); margin-top: 2px; }
body.portal-v3 .v3d-hero__cuerpo {
  display: flex;
  align-items: center;
  gap: 22px;
  flex-wrap: wrap;
  margin-top: 16px;
}
body.portal-v3 .v3d-hero__pac { display: flex; align-items: center; gap: 16px; flex: 1; min-width: 280px; }
body.portal-v3 .v3d-hero__pac h3 {
  margin: 0;
  font: 600 1.28rem/1.2 var(--fuente-titulo, "Poppins", sans-serif);
  color: var(--tinta, #1e1a3f);
}
body.portal-v3 .v3d-hero__motivo { margin-top: 8px; }
body.portal-v3 .v3d-hero__lado {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
  border-left: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  padding-left: 22px;
}
@media (max-width: 820px) { body.portal-v3 .v3d-hero__lado { border-left: 0; padding-left: 0; } }
body.portal-v3 .v3d-hero__hora { display: flex; align-items: center; gap: 11px; }
body.portal-v3 .v3d-hero__hora .h {
  font: 600 1.32rem/1.15 var(--fuente-titulo, "Poppins", sans-serif);
  color: var(--tinta, #1e1a3f);
  font-variant-numeric: tabular-nums;
}
body.portal-v3 .v3d-hero__acc { display: flex; gap: 8px; flex-wrap: wrap; }

/* ── KPIs ────────────────────────────────────────────────────────────────── */
body.portal-v3 .v3d-kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
@media (max-width: 1080px) { body.portal-v3 .v3d-kpis { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { body.portal-v3 .v3d-kpis { grid-template-columns: 1fr; } }
body.portal-v3 .v3d-kpi {
  background: #fff;
  border-radius: var(--r, 18px);
  box-shadow: var(--sombra-sm, 0 6px 18px rgba(91, 79, 207, .10));
  padding: 16px 15px;
  border: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
  display: flex;
  gap: 11px;
  align-items: flex-start;
  width: 100%;
  transition: transform .15s, box-shadow .15s;
}
body.portal-v3 .v3d-kpi.es-quieto { cursor: default; }
body.portal-v3 .v3d-kpi:not(.es-quieto):hover {
  transform: translateY(-2px);
  box-shadow: var(--sombra, 0 16px 40px rgba(91, 79, 207, .16));
}
body.portal-v3 .v3d-kpi > .v3d-chipic { width: 38px; height: 38px; border-radius: 11px; }
body.portal-v3 .v3d-kpi__txt { min-width: 0; flex: 1; }
/* display:block obligatorio: son <span> y sin esto el rótulo y la cifra quedan
   en la misma línea ("Consultas hoy 12"). */
body.portal-v3 .v3d-kpi__rot { display: block; font-size: .77rem; color: var(--v3d-meta); font-weight: 500; }
body.portal-v3 .v3d-kpi__val {
  display: block;
  font: 600 1.55rem/1.2 var(--fuente-titulo, "Poppins", sans-serif);
  color: var(--tinta, #1e1a3f);
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}
body.portal-v3 .v3d-kpi__pie { display: flex; align-items: flex-end; gap: 8px; margin-top: 6px; }
body.portal-v3 .v3d-kpi__pie small {
  font-size: .74rem;
  color: var(--v3d-meta);
  line-height: 1.35;
  white-space: pre-line;              /* el "\n" del pie parte la línea */
}
@media (min-width: 1560px) {
  body.portal-v3 .v3d-kpi__rot { white-space: nowrap; }
  body.portal-v3 .v3d-sem__met span { white-space: nowrap; }
}

/* ── Tablas ──────────────────────────────────────────────────────────────── */
body.portal-v3 .v3d-tabla-caja { overflow-x: auto; margin: 6px -6px 0; }
body.portal-v3 .v3d-tabla { width: 100%; border-collapse: collapse; }
body.portal-v3 .v3d-tabla th {
  text-align: left;
  font: 600 .83rem/1.3 var(--fuente, Inter, sans-serif);
  color: var(--v3d-meta);
  padding: 10px 8px;
  border-bottom: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  white-space: nowrap;
}
body.portal-v3 .v3d-tabla td {
  padding: 12px 8px;
  border-bottom: 1px solid var(--linea, rgba(91, 79, 207, .14));
  font-size: .9rem;
  color: var(--tinta, #1e1a3f);
  vertical-align: middle;
}
body.portal-v3 .v3d-tabla tr:last-child td { border-bottom: 0; }
body.portal-v3 .v3d-tabla .es-der { text-align: right; }
body.portal-v3 .v3d-hora { font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
body.portal-v3 .v3d-pac { display: flex; align-items: center; gap: 11px; }
body.portal-v3 .v3d-pac b { display: block; font-size: .92rem; line-height: 1.3; }
body.portal-v3 .v3d-pac .v3d-meta { font-size: .78rem; margin: 0; }
body.portal-v3 .v3d-acc { display: inline-flex; align-items: center; gap: 6px; justify-content: flex-end; flex-wrap: wrap; }
body.portal-v3 .v3d-mini-ok { font-size: .78rem; color: var(--v3d-verde); font-weight: 600; white-space: nowrap; }

/* ── Resumen semanal ─────────────────────────────────────────────────────── */
body.portal-v3 .v3d-sem {
  display: grid;
  grid-template-columns: 252px minmax(0, 1fr) 270px;
  gap: 22px;
  align-items: start;
  margin-top: 10px;
}
@media (max-width: 1180px) { body.portal-v3 .v3d-sem { grid-template-columns: 1fr; } }
body.portal-v3 .v3d-sem__rango { margin: 0 0 6px; }
body.portal-v3 .v3d-sem__met {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 0;
  border-bottom: 1px solid var(--linea, rgba(91, 79, 207, .14));
}
body.portal-v3 .v3d-sem__met:last-child { border-bottom: 0; }
body.portal-v3 .v3d-sem__met span { font-size: .88rem; color: var(--v3d-meta); flex: 1; }
body.portal-v3 .v3d-sem__met b {
  font: 600 1rem/1.2 var(--fuente-titulo, "Poppins", sans-serif);
  font-variant-numeric: tabular-nums;
}
body.portal-v3 .v3d-graf { min-width: 0; }
body.portal-v3 .v3d-dona { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
body.portal-v3 .v3d-dona__svg { width: 132px; height: 132px; flex: none; }
body.portal-v3 .v3d-dona__ley { flex: 1; min-width: 150px; display: flex; flex-direction: column; gap: 7px; }
body.portal-v3 .v3d-dona__it { display: flex; align-items: center; gap: 8px; font-size: .82rem; color: var(--v3d-meta); }
body.portal-v3 .v3d-dona__it i { width: 9px; height: 9px; border-radius: 50%; flex: none; }
body.portal-v3 .v3d-dona__it b { margin-left: auto; color: var(--tinta, #1e1a3f); font-variant-numeric: tabular-nums; }

/* ── Listas de la columna derecha ────────────────────────────────────────── */
body.portal-v3 .v3d-it {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  padding: 12px 10px;
  border-radius: var(--r-sm, 12px);
  text-align: left;
}
body.portal-v3 .v3d-it--btn { border: 0; background: none; font: inherit; cursor: pointer; }
body.portal-v3 .v3d-it--btn:hover { background: #faf9ff; }
body.portal-v3 .v3d-it__ic { flex: none; }
body.portal-v3 .v3d-it__ic .v3d-chipic { width: 36px; height: 36px; border-radius: 10px; }
body.portal-v3 .v3d-it__tx { flex: 1; min-width: 0; }
body.portal-v3 .v3d-it__tx b { display: block; font-size: .9rem; font-weight: 600; line-height: 1.35; }
body.portal-v3 .v3d-it__tx small { display: block; font-size: .81rem; color: var(--v3d-meta); }
body.portal-v3 .v3d-it__cuando { font-size: .76rem; color: var(--v3d-meta); flex: none; padding-top: 2px; white-space: nowrap; }

/* ── Consulta de prueba (FASE 62a) ────────────────────────────────────────
   Acá vivía la grilla 3×2 de "Acciones rápidas" del Inicio v3, retirada por
   orden directa de Mauricio. Su lugar lo toma la tarjeta "Probar una consulta",
   que reusa .v3d-card completa: lo único propio es el aire sobre el botón (y va
   scoped al id, para no aflojarle el espaciado a ninguna otra tarjeta). */
body.portal-v3 #v3d-demo .btn { margin-top: 12px; }
/* 🔴 FASE 62a2 · SOLO PARA LECTORES DE PANTALLA. No existía nada así en el
   portal (0 hits de sr-only/clip en las hojas), y hacía falta para el aria-live
   de la tarjeta demo.
   🔴 JAMÁS display:none ni visibility:hidden: un aria-live dentro de un nodo
   apagado así NO anuncia nada — el lector lo trata como inexistente y el aviso
   se pierde en silencio (que es el peor modo de fallar: se ve igual que si
   funcionara). El patrón de abajo mantiene el nodo RENDERIZADO y fuera de
   pantalla, que es lo que sí anuncia. */
body.portal-v3 .v3-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* La tarjeta recibe el foco (tabindex="-1") cuando el botón se deshabilita: sin
   eso activeElement se cae a BODY y el lector pierde el hilo. El anillo es el
   mismo de todo lo clickeable nuevo. */
body.portal-v3 #v3d-demo:focus-visible {
  outline: 3px solid var(--violeta, #5b4fcf);
  outline-offset: 2px;
}

/* ── Documentos ──────────────────────────────────────────────────────────── */
body.portal-v3 .v3d-docs__nota { margin: 0 0 14px; }
body.portal-v3 .v3d-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
body.portal-v3 .v3d-chip {
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  background: #fff;
  border-radius: 999px;
  font: 600 .84rem/1.2 var(--fuente, Inter, sans-serif);
  color: var(--v3d-meta);
  padding: 8px 15px;
  cursor: pointer;
}
body.portal-v3 .v3d-chip:hover { border-color: #cdc6f2; color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-chip.is-on {
  background: var(--violeta, #5b4fcf);
  border-color: var(--violeta, #5b4fcf);
  color: #fff;                        /* 6,06 */
  cursor: default;
}
body.portal-v3 .v3d-docpt { display: inline-flex; align-items: center; gap: 9px; font-weight: 600; font-size: .9rem; }
body.portal-v3 .v3d-docpt i { width: 10px; height: 24px; border-radius: 4px; display: block; flex: none; }
body.portal-v3 .v3d-folio {
  font: 600 .86rem/1.3 var(--fuente-titulo, "Poppins", sans-serif);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
body.portal-v3 .v3d-hoja {
  border: 1px solid var(--linea, rgba(91, 79, 207, .14));
  border-radius: var(--r-sm, 12px);
  padding: 16px 18px;
  background: #fdfcff;
}
body.portal-v3 .v3d-hoja__mem {
  font: 700 .78rem/1.2 var(--fuente-titulo, "Poppins", sans-serif);
  color: var(--violeta, #5b4fcf);
  letter-spacing: .04em;
  text-transform: uppercase;
  margin-bottom: 8px;
}
body.portal-v3 .v3d-hoja h4 {
  margin: 12px 0 2px;
  font: 700 .74rem/1.2 var(--fuente, Inter, sans-serif);
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--v3d-meta);
}
body.portal-v3 .v3d-hoja p { margin: 0; font-size: .92rem; color: var(--tinta, #1e1a3f); }

/* ── Estadísticas ────────────────────────────────────────────────────────── */
body.portal-v3 .v3d-kpis--fijos { margin-top: 10px; }
body.portal-v3 .v3d-est-graf {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: 20px;
  align-items: start;
  margin-top: 20px;
}
@media (max-width: 1080px) { body.portal-v3 .v3d-est-graf { grid-template-columns: 1fr; } }
body.portal-v3 .v3d-barras { display: flex; align-items: flex-end; gap: 12px; height: 190px; margin-top: 16px; }
body.portal-v3 .v3d-barras > div {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
body.portal-v3 .v3d-barras .b {
  width: 100%;
  border-radius: 9px 9px 0 0;
  background: var(--grad-primario, linear-gradient(135deg, #5b4fcf, #7a6ee0));
  min-height: 6px;
}
body.portal-v3 .v3d-barras b { font-size: .82rem; font-variant-numeric: tabular-nums; }
body.portal-v3 .v3d-barras small { font-size: .75rem; color: var(--v3d-meta); }

/* ── Ajustes ─────────────────────────────────────────────────────────────── */
body.portal-v3 .v3d-ajustes {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
  gap: 20px;
  align-items: start;
}
body.portal-v3 .v3d-perfil { display: flex; align-items: center; gap: 16px; margin: 8px 0 4px; }
body.portal-v3 .v3d-perfil b { font-size: 1.05rem; }
body.portal-v3 .v3d-perfil__foto {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  object-fit: cover;
  flex: none;
}
body.portal-v3 .v3d-perfil__acc { margin-top: 14px; }
body.portal-v3 .v3d-file { cursor: pointer; }
body.portal-v3 .v3d-pares { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 12px; }
/* 🔴 62f — el correo es el dato más largo del bloque y en media columna se
   partía a mitad de palabra. Ocupa la fila entera y solo se corta si de verdad
   no cabe (word-break:normal impide el corte gratuito dentro de la palabra). */
body.portal-v3 .v3d-par--ancho { grid-column: 1 / -1; }
body.portal-v3 .v3d-par--ancho b { overflow-wrap: break-word; word-break: normal; }
@media (max-width: 760px) { body.portal-v3 .v3d-pares { grid-template-columns: 1fr; } }
body.portal-v3 .v3d-par {
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  border-radius: var(--r-sm, 12px);
  padding: 12px 14px;
  min-width: 0;
}
body.portal-v3 .v3d-par small {
  display: block;
  font: 700 .75rem/1.2 var(--fuente, Inter, sans-serif);
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--v3d-meta);
  margin-bottom: 3px;
}
/* 🔴 FASE 62c3 (D5) — `anywhere` cortaba el correo a mitad de palabra
   ("amaya@nexofem.co / m") aunque cupiera entero en la línea siguiente: ese
   valor permite cortar en cualquier carácter y cuenta para el ancho mínimo.
   `break-word` corta SOLO si la palabra no cabe de ninguna forma. */
body.portal-v3 .v3d-par b { font-size: .92rem; font-weight: 500; overflow-wrap: break-word; }

/* ── Foco visible en todo lo clickeable nuevo ────────────────────────────── */
body.portal-v3 .v3d-kpi:focus-visible,
body.portal-v3 .v3d-chip:focus-visible,
body.portal-v3 .v3d-lnk:focus-visible,
body.portal-v3 .v3d-volver:focus-visible,
body.portal-v3 .v3d-it--btn:focus-visible,
body.portal-v3 .v3d-campana:focus-visible,
body.portal-v3 .v3d-quien:focus-visible,
body.portal-v3 .v3d-sel:focus-visible,
body.portal-v3 .v3d-buscar input:focus-visible,
body.portal-v3 .v3d-buscar__go:focus-visible {
  outline: 3px solid var(--violeta, #5b4fcf);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  body.portal-v3 .v3d-kpi:hover { transform: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   FASE 61 · VIDEO PERSISTENTE
   Todo cuelga de body.portal-v3: el portal clásico de Margot no ve una sola
   de estas reglas. Este bloque va AL FINAL del archivo a propósito: varias
   reglas empatan en especificidad con las del cajón (E2) y con las del modo
   jitsi de estilos.css, y en un empate gana la última declarada.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── (1) La sala DE FONDO ─────────────────────────────────────────────────
   🔴 JAMÁS display:none: un position:fixed dentro de un ancestro con
   display:none NO SE PINTA, y el mini-video desaparecería al navegar. Se saca
   del flujo y se apaga con visibility, que SÍ se puede revertir en un
   descendiente (es lo que hace la regla siguiente con el mini). */
body.portal-v3 #pantalla-sesion.v3f-fondo {
  display: block;
  position: absolute;
  left: 0;
  top: 0;
  width: 1px;
  height: 1px;
  overflow: visible;
  visibility: hidden;
  pointer-events: none;
  z-index: 0;
}
body.portal-v3 #pantalla-sesion.v3f-fondo .sesion-video.v3f-mini {
  visibility: visible;
  pointer-events: auto;
}

/* ── (2) El mini flotante ─────────────────────────────────────────────────
   El nodo es el MISMO .sesion-video de siempre: solo cambia de clase. Sus
   hijos se apagan salvo el escenario (que contiene el iframe de Jitsi) y la
   barra del mini. */
body.portal-v3 .sesion-video.v3f-mini {
  position: fixed !important;
  width: 300px;
  height: 190px;
  min-height: 0;
  max-height: none;
  margin: 0;
  padding: 0;
  z-index: 200;
  border-radius: 14px;
  overflow: hidden;
  background: #1e1a3f;
  border: 2px solid rgba(255, 255, 255, .55);
  box-shadow: 0 18px 44px rgba(23, 19, 46, .45);
  align-self: auto !important;
}
/* Con un modal abierto (.overlay, z-index 80) el mini BAJA: si no, taparía el
   botón del propio modal que la médica acaba de abrir. */
body.portal-v3 .sesion-video.v3f-mini.v3f-bajo { z-index: 40; }
body.portal-v3 .sesion-video.v3f-mini.v3f-arrastrando {
  box-shadow: 0 24px 56px rgba(23, 19, 46, .55);
  transition: none;
}
body.portal-v3 .sesion-video.v3f-mini > * { display: none !important; }
body.portal-v3 .sesion-video.v3f-mini > #ses-escenario {
  display: block !important;
  margin: 0 !important;
  width: 100% !important;
  height: 190px !important;
  min-height: 190px !important;
  max-height: 190px !important;
  aspect-ratio: auto !important;   /* el HTML lo trae inline (16/10) */
  border-radius: 0 !important;
}
body.portal-v3 .sesion-video.v3f-mini > #v3f-mini { display: flex !important; }

/* ── (3) La barra del mini ────────────────────────────────────────────────
   Es la superficie de arrastre: el cuerpo del mini es el iframe de Jitsi y un
   iframe se queda con sus propios eventos de puntero. */
body.portal-v3 #v3f-mini {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  align-items: center;
  gap: 8px;
  padding: 7px 9px;
  background: rgba(23, 19, 46, .88);
  color: #fff;
  font: 600 .78rem/1.2 Inter, sans-serif;
  cursor: grab;
  z-index: 2;
  touch-action: none;
}
body.portal-v3 .sesion-video.v3f-mini.v3f-arrastrando #v3f-mini { cursor: grabbing; }
body.portal-v3 .v3f-mini__nom {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 96px;
}
body.portal-v3 .v3f-crono {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  background: rgba(255, 255, 255, .16);
  border-radius: 8px;
  padding: 2px 8px;
  display: inline-flex;
  align-items: center;
}
body.portal-v3 .v3f-crono[hidden] { display: none !important; }
body.portal-v3 .v3f-btn {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: none;
  border: 0;
  border-radius: 9px;
  background: rgba(255, 255, 255, .18);
  color: #fff;
  cursor: pointer;
}
body.portal-v3 .v3f-btn + .v3f-btn { margin-left: 0; }
body.portal-v3 .v3f-btn:hover { background: rgba(255, 255, 255, .30); }
body.portal-v3 .v3f-btn:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
body.portal-v3 .v3f-btn--rojo { background: #c0392b; }
body.portal-v3 .v3f-btn--rojo:hover { background: #a93226; }
body.portal-v3 .v3f-btn[hidden] { display: none !important; }
body.portal-v3 .v3f-btn .v3d-ic svg { width: 100%; height: 100%; }

/* ── (4) El punto "en vivo" ───────────────────────────────────────────────
   Indicador NO textual: el dato lo dice el texto que lo acompaña ("En consulta
   con…", "Consulta en curso"). El color no carga información por sí solo. */
body.portal-v3 .v3f-vivo {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #ff6b5e;
  flex: none;
  box-shadow: 0 0 10px #ff6b5e;
  animation: v3f-lat 1.6s infinite;
}
@keyframes v3f-lat {
  0%, 100% { opacity: 1; }
  50% { opacity: .35; }
}
@media (prefers-reduced-motion: reduce) {
  body.portal-v3 .v3f-vivo { animation: none; }
}

/* ── (5) La franja de consulta activa ─────────────────────────────────────
   Fija bajo la topbar global, en TODAS las vistas menos la sala. z-index 38:
   por debajo de la topbar (39) y del lateral (40), por encima del contenido. */
body.portal-v3 #v3f-franja {
  display: none;
  position: fixed;
  left: var(--v3-lat-w);
  right: 0;
  top: var(--v3d-top-h);
  z-index: 38;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  min-height: 48px;
  padding: 9px 28px;
  background: linear-gradient(135deg, #2b2456, #1e1a3f);
  color: #fff;
  box-shadow: 0 10px 26px rgba(30, 26, 63, .28);
}
body.portal-v3 #v3f-franja.es-on { display: flex; }
body.portal-v3 #v3f-franja[hidden] { display: none !important; }
body.portal-v3 .v3f-franja__tx { font: 600 .94rem/1.3 Inter, sans-serif; }
body.portal-v3 #v3f-franja-volver { margin-left: auto; }
/* El contenido baja lo que mide la franja para que no le tape el encabezado.
   Mismo criterio (y misma media) que el corrimiento de la topbar global. */
@media (min-width: 980px) {
  body.portal-v3.v3-con-shell.v3f-con-consulta:not(.v3-en-sala) main {
    padding-top: calc(var(--v3d-top-h) + 48px);
  }
}

/* ── (6) El hueco que deja el video en la sala ────────────────────────────
   Solo existe mientras el video está en la esquina: sin él, la sala salta
   varios cientos de píxeles hacia arriba. */
body.portal-v3 #v3f-hueco { display: none; }
body.portal-v3 #v3f-hueco.es-on {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  text-align: center;
  padding: 20px;
  align-self: start;
  /* #8279c2 y no el #cdc6f2 del mockup: ese daba 1,56 sobre #fbfaff y el
     borde es lo único que delimita el hueco. Recalculado: 3,70 (min 3,0). */
  border: 2px dashed #8279c2;
  border-radius: 16px;
  background: #fbfaff;
  color: #605a7d;
  font: 600 .9rem/1.45 Inter, sans-serif;
}
body.portal-v3 .v3f-hueco__tx { margin: 0; }

/* ── (7) El item "Consulta en curso" del lateral ──────────────────────────
   Reusa .v3-lat-it (F57, aprobado). El punto vivo se va al extremo derecho. */
body.portal-v3 .v3f-lat-it .v3f-vivo { margin-left: auto; }
body.portal-v3 .v3f-lat-it[hidden] { display: none !important; }

/* ── (8) scroll-margin de los destinos de v3Scroll ────────────────────────
   La topbar de la sala es sticky y la franja es fija: sin esto, un
   scrollIntoView deja el encabezado del destino DEBAJO de la barra. */
body.portal-v3 #ses-resultado,
body.portal-v3 #v3-pl-firma,
body.portal-v3 #v3-sec-receta,
body.portal-v3 #v3-sec-orden,
body.portal-v3 #v3-sec-control {
  scroll-margin-top: calc(var(--v3-topbar-h) + 24px);
}
body.portal-v3 #v3d-notis { scroll-margin-top: calc(var(--v3d-top-h) + 24px); }
body.portal-v3.v3f-con-consulta #v3d-notis {
  scroll-margin-top: calc(var(--v3d-top-h) + 72px);
}

/* ── Historia clínica in-place (FASE 62b) ─────────────────────────────────
   Se REUSA todo lo que ya existe: .v3d-card, .v3d-card__cab, .v3d-sel,
   .v3d-vacio, .v3d-it*, .v3d-tabla*, .v3d-pill*, .v3d-barras. Lo de abajo es
   solo lo que el mockup pide y no estaba.

   🔴 PREFIJO v3d- OBLIGATORIO: el mockup llama a estas clases .evo, .barra,
   .col y .leyenda, y esos cuatro nombres YA EXISTEN en el portal vivo
   (sala.css / el layout). Redefinirlos acá le cambiaría la cara a la sala.
   Misma lección que dejó la familia .hvmrs-* en la Fase 38.

   🔴 CONTRASTES RECALCULADOS (regla de la casa): el mockup pinta las columnas
   con #b9b1ec (1,99:1 sobre #fff) y #d9d4f5 (1,43:1). Ninguno llega al 3:1 que
   exige el estándar para un objeto gráfico que porta información. Se reemplazan
   por #6f63d6 (4,74:1) y #8b81d8 (3,37:1): misma familia violeta y MISMA
   jerarquía (la más reciente sigue siendo la más oscura, con el degradado de
   marca). Desviación D-62b-7, declarada. */
body.portal-v3 .v3d-card--sep { margin-top: 20px; }

/* Cabecera de la paciente (mockup .ficha__cab / .ficha__datos) */
body.portal-v3 .v3d-fichacab {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  margin-top: 8px;
}
body.portal-v3 .v3d-fichacab__id { min-width: 0; }
body.portal-v3 .v3d-fichacab__id h2 {
  font: 600 1.35rem/1.25 var(--fuente-titulo, "Poppins", sans-serif);
  color: var(--tinta, #1e1a3f);          /* 16,47 sobre #fff */
  margin: 0;
}
body.portal-v3 .v3d-fichadatos { display: flex; gap: 28px; flex-wrap: wrap; margin-left: auto; }
body.portal-v3 .v3d-fichadato small {
  display: block;
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--v3d-meta);                /* 6,45 sobre #fff */
}
body.portal-v3 .v3d-fichadato b {
  font-size: .92rem;
  font-weight: 600;
  color: var(--tinta, #1e1a3f);
}
body.portal-v3 .v3d-ava--56 { width: 56px; height: 56px; font-size: 1.15rem; }

/* Bloque MRS (mockup .mrs__num / .barra / .evo / .leyenda) */
body.portal-v3 .v3d-mrsnum { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
body.portal-v3 .v3d-mrsnum b {
  font: 600 2rem/1 var(--fuente-titulo, "Poppins", sans-serif);
  color: var(--tinta, #1e1a3f);
  font-variant-numeric: tabular-nums;
}
body.portal-v3 .v3d-mrsnum .v3d-pill { margin-left: auto; }
body.portal-v3 .v3d-barra { height: 9px; border-radius: 999px; background: #ece9f8; overflow: hidden; margin-top: 7px; }
body.portal-v3 .v3d-barra i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--grad-primario, linear-gradient(135deg, #5b4fcf, #7a6ee0));
}
body.portal-v3 .v3d-mrsfrase { margin-top: 10px; }
body.portal-v3 .v3d-barras--evo { height: 150px; margin-top: 22px; }
body.portal-v3 .v3d-barras--evo .b.es-inicial { background: #6f63d6; }   /* 4,74 sobre #fff */
body.portal-v3 .v3d-barras--evo .b { background: #8b81d8; }              /* 3,37 sobre #fff */
body.portal-v3 .v3d-barras--evo .b.es-hoy {
  background: var(--grad-primario, linear-gradient(135deg, #5b4fcf, #7a6ee0));   /* 6,06 */
}
body.portal-v3 .v3d-leyenda {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 12px;
  font-size: .78rem;
  color: var(--v3d-meta);
}
body.portal-v3 .v3d-leyenda span { display: flex; align-items: center; gap: 6px; }
body.portal-v3 .v3d-leyenda i { width: 11px; height: 11px; border-radius: 3px; display: block; flex: none; }
body.portal-v3 .v3d-leyenda i.es-inicial { background: #6f63d6; }
body.portal-v3 .v3d-leyenda i.es-ctrl { background: #8b81d8; }
body.portal-v3 .v3d-leyenda i.es-hoy { background: var(--grad-primario, linear-gradient(135deg, #5b4fcf, #7a6ee0)); }

@media (max-width: 720px) {
  body.portal-v3 .v3d-fichadatos { margin-left: 0; gap: 18px; }
  body.portal-v3 .v3d-card__cab .v3d-sel { margin-left: 0; }
}

/* ── FASE 62c · kebab ⋮, confirmación de 2 clics, Ajustes, pulido Historia ──
   Se REUSA todo lo que ya existe: .v3d-card, .v3d-menu (¡el estilo del menú del
   avatar!), .v3d-pill*, .v3d-pares, .v3d-lnk, .v3d-pie-card, .v3d-barras.
   Lo de abajo es solo lo que faltaba.

   🔴 .v3d-menu NO se redefine: el componente kebab emite `class="v3d-menu
   v3d-kmenu"` y hereda el estilo que ya viste el menú del avatar. La clase
   propia existe para que el CIERRE del kebab no toque ese otro menú (si lo
   cerrara, "Cerrar sesión" dejaría de abrirse).

   🔴 CONTRASTES RECALCULADOS (regla de la casa) — ver verificar-fase62.py:
     · ⋮ #605a7d sobre #fff .......... 6,45  (texto)
     · ⋮ hover #1e1a3f sobre #f2f0fb .. 15,3
     · punto de severidad alta: currentColor del pill (#5b4fcf) sobre #ede9ff
       = 5,11 — muy por encima del 3:1 que pide un objeto gráfico.
     · botón en confirmación: #6b5200 sobre #fbf0da = 6,56 (el mismo par ámbar
       que ya usa .v3d-pill--aviso; NO se inventa un color nuevo).            */

/* ── Kebab ⋮ ──────────────────────────────────────────────────────────────── */
body.portal-v3 .v3d-kebab-env { position: relative; display: inline-flex; flex: none; }
body.portal-v3 .v3d-kebab {
  width: 34px;
  height: 34px;
  border-radius: 10px;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--v3d-meta);              /* 6,45 sobre #fff */
  font-size: 1.15rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
body.portal-v3 .v3d-kebab:hover { background: #f2f0fb; color: var(--tinta, #1e1a3f); }
body.portal-v3 .v3d-kebab:focus-visible {
  outline: 3px solid var(--violeta, #5b4fcf);
  outline-offset: 2px;
}
/* El menú del kebab se ancla a su envoltura (el del avatar se ancla a la suya). */
body.portal-v3 .v3d-kmenu { right: 0; }
body.portal-v3 .v3d-hero__kebab { margin-left: auto; }

/* ── Confirmación de dos clics (mismo gesto que el Sobre de la sala) ──────── */
body.portal-v3 .v3d-confirmando {
  background: var(--durazno-claro, #fbf0da);
  color: var(--v3d-ambar);             /* 6,56 sobre #fbf0da */
  border-color: var(--v3d-ambar);
  font-weight: 600;
}

/* ── KPIs del INICIO: los rótulos de dos líneas desalineaban las cifras ────
   "Documentos pendientes" ocupa dos líneas y empujaba su número medio renglón
   más abajo que los otros tres. Se reserva el alto de dos líneas para los
   cuatro. 🔴 Scoped al Inicio: en Estadísticas los cuatro rótulos son de una
   línea y reservar dos dejaría un hueco. */
body.portal-v3 #pantalla-dashboard .v3d-kpi__rot { min-height: 2.2em; }

/* ── Severidad alta: canal NO cromático (punto de 8 px del mockup) ────────── */
body.portal-v3 .v3d-pill--punto::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

/* ── Historia · estado ÚNICO de error de la vista (pulido 1) ──────────────── */
body.portal-v3 .v3d-errvista {
  text-align: center;
  padding: 26px 18px;
  color: var(--v3d-meta);
}
body.portal-v3 .v3d-errvista .v3d-chipic { width: 44px; height: 44px; border-radius: 13px; margin: 0 auto 10px; }
body.portal-v3 .v3d-errvista .v3d-chipic svg { width: 21px; height: 21px; }
body.portal-v3 .v3d-errvista b { display: block; font-size: .98rem; margin-bottom: 4px; color: var(--tinta, #1e1a3f); }
body.portal-v3 .v3d-errvista span { font-size: .88rem; }
body.portal-v3 .v3d-errvista__acc { margin-top: 12px; }

/* ── Historia · aviso inline con ícono (pulido 8) ─────────────────────────── */
body.portal-v3 .v3d-aviso-inline {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 10px 0 0;
  padding: 9px 11px;
  border-radius: 10px;
  background: var(--durazno-claro, #fbf0da);
  color: var(--v3d-ambar);             /* 6,56 sobre #fbf0da */
  font-size: .84rem;
}
body.portal-v3 .v3d-aviso-inline svg { width: 14px; height: 14px; flex: none; }

/* ── Ajustes · tarjeta Avisos (informativa, sin controles) ────────────────── */
body.portal-v3 .v3d-avisos { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
body.portal-v3 .v3d-aviso {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 13px;
  border-radius: var(--r-sm, 12px);
  background: #faf9ff;
}
body.portal-v3 .v3d-aviso > span { flex: 1; min-width: 0; }
body.portal-v3 .v3d-aviso b { display: block; font-size: .9rem; font-weight: 600; color: var(--tinta, #1e1a3f); }
body.portal-v3 .v3d-aviso small { display: block; font-size: .81rem; color: var(--v3d-meta); }
body.portal-v3 .v3d-ultacc { margin-top: 14px; }

/* ── Ajustes · campos del modal de contraseña ─────────────────────────────── */
body.portal-v3 .v3d-campo { display: block; margin-top: 12px; }
body.portal-v3 .v3d-campo span {
  display: block;
  font-size: .8rem;
  font-weight: 600;
  color: var(--v3d-meta);
  margin-bottom: 5px;
}
body.portal-v3 .v3d-campo input {
  width: 100%;
  font: 400 .95rem/1.3 var(--fuente, Inter, sans-serif);
  color: var(--tinta, #1e1a3f);
  background: #fff;
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  border-radius: 10px;
  padding: 10px 12px;
}
body.portal-v3 .v3d-campo input:focus-visible {
  outline: 3px solid var(--violeta, #5b4fcf);
  outline-offset: 1px;
}

/* ── FASE 62d-2 · Mis pacientes (tabla ordenable) ─────────────────────────
   Se REUSA todo: .v3d-card, .v3d-tabla, .v3d-pac, .v3d-acc, .v3d-pill,
   .v3d-kebab (62c), .v3d-vacio, .v3d-contador. Lo de abajo es lo que faltaba.

   🔴 CONTRASTES RECALCULADOS (ver verificar-fase62.py):
     · cabecera ordenable #605a7d sobre #fff ....... 6,45
     · cabecera activa    #5b4fcf sobre #fff ....... 6,06
     · badge de alerta    #6b5200 sobre #fbf0da .... 6,56  (par ámbar ya vigente)
   🔴 La flechita ⇅ NO es el único indicador del orden: el th lleva aria-sort y
   la columna activa además cambia de color y de peso. Tres canales, ninguno
   dependiente de ver la flecha. */
body.portal-v3 .v3d-pacbusca { margin: 4px 0 14px; }
body.portal-v3 .v3d-input {
  width: 100%;
  max-width: 420px;
  font: 400 .92rem/1.3 var(--fuente, Inter, sans-serif);
  color: var(--tinta, #1e1a3f);
  background: #fff;
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  border-radius: 10px;
  padding: 10px 13px;
}
body.portal-v3 .v3d-input:focus-visible {
  outline: 3px solid var(--violeta, #5b4fcf);
  outline-offset: 1px;
}
body.portal-v3 .v3d-th-ord {
  border: 0;
  background: none;
  font: inherit;
  font-weight: 600;
  color: var(--v3d-meta);              /* 6,45 sobre #fff */
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
body.portal-v3 .v3d-th-ord:hover { color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-th-ord:focus-visible {
  outline: 3px solid var(--violeta, #5b4fcf);
  outline-offset: 2px;
  border-radius: 6px;
}
body.portal-v3 [aria-sort="ascending"] .v3d-th-ord,
body.portal-v3 [aria-sort="descending"] .v3d-th-ord { color: var(--violeta, #5b4fcf); }  /* 6,06 */
body.portal-v3 .v3d-flechita { font-size: .8rem; opacity: .9; }
body.portal-v3 .v3d-alerta {
  display: inline-flex;
  align-items: center;
  color: var(--v3d-ambar);             /* 6,56 sobre #fbf0da */
  background: var(--durazno-claro, #fbf0da);
  border-radius: 6px;
  padding: 1px 4px;
  margin-left: 4px;
}
body.portal-v3 .v3d-alerta svg { width: 13px; height: 13px; }

/* ── FASE 62d-3 · Mensajes en dos columnas ────────────────────────────────
   Reusa .v3d-card, .v3d-vacio, .v3d-ava, .v3d-lnk, .v3d-meta, .btn.
   🔴 CONTRASTES RECALCULADOS: hilo activo #5b4fcf sobre #f3f0ff = 5,4 ·
   burbuja de la paciente #1e1a3f sobre #ede9ff = 13,3 · badge #fff sobre
   #b3261e = 6,54 · punto de no leídos #fff sobre #5b4fcf = 6,06.
   🔴 El hilo con mensajes sin leer NO se distingue solo por color: lleva el
   NÚMERO en un punto y el nombre en negrita. */
body.portal-v3 .v3d-msj {
  display: grid;
  grid-template-columns: minmax(0, 320px) minmax(0, 1fr);
  gap: 18px;
  margin-top: 14px;
  align-items: start;
}
@media (max-width: 900px) { body.portal-v3 .v3d-msj { grid-template-columns: 1fr; } }
body.portal-v3 .v3d-msj__lista {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 62vh;
  overflow: auto;
}
body.portal-v3 .v3d-msj__it {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  font: inherit;
  padding: 10px 11px;
  border-radius: var(--r-sm, 12px);
  cursor: pointer;
}
body.portal-v3 .v3d-msj__it:hover { background: #faf9ff; }
body.portal-v3 .v3d-msj__it.es-on { background: #f3f0ff; }
body.portal-v3 .v3d-msj__it.es-on b { color: var(--violeta, #5b4fcf); }   /* 5,4 */
body.portal-v3 .v3d-msj__it:focus-visible {
  outline: 3px solid var(--violeta, #5b4fcf);
  outline-offset: -1px;
}
body.portal-v3 .v3d-msj__tx { flex: 1; min-width: 0; }
body.portal-v3 .v3d-msj__tx b { display: block; font-size: .9rem; font-weight: 600; }
body.portal-v3 .v3d-msj__it.es-nuevo .v3d-msj__tx b { font-weight: 700; }
body.portal-v3 .v3d-msj__tx small {
  display: block;
  font-size: .81rem;
  color: var(--v3d-meta);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.portal-v3 .v3d-msj__meta {
  flex: none;
  text-align: right;
  font-size: .74rem;
  color: var(--v3d-meta);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
}
body.portal-v3 .v3d-msj__punto {
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--violeta, #5b4fcf);
  color: #fff;                          /* 6,06 */
  font-size: .7rem;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
body.portal-v3 .v3d-msj__conv {
  border-left: 1px solid var(--linea, rgba(91, 79, 207, .14));
  padding-left: 18px;
  min-height: 300px;
}
@media (max-width: 900px) {
  body.portal-v3 .v3d-msj__conv { border-left: 0; padding-left: 0; border-top: 1px solid var(--linea, rgba(91, 79, 207, .14)); padding-top: 14px; }
}
body.portal-v3 .v3d-msj__cab { display: flex; align-items: center; gap: 11px; margin-bottom: 12px; }
body.portal-v3 .v3d-msj__quien { flex: 1; min-width: 0; }
body.portal-v3 .v3d-msj__quien b { display: block; font-size: .95rem; font-weight: 600; }
body.portal-v3 .v3d-msj__quien small { display: block; font-size: .8rem; color: var(--v3d-meta); }
body.portal-v3 .v3d-msj__chat {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 46vh;
  overflow: auto;
  padding: 4px;
}
body.portal-v3 .v3d-burb {
  max-width: 82%;
  padding: 10px 13px;
  border-radius: var(--r, 18px);
  font-size: .92rem;
  line-height: 1.5;
}
body.portal-v3 .v3d-burb.es-med { align-self: flex-end; background: #fff; border: 1px solid var(--linea, rgba(91, 79, 207, .14)); }
body.portal-v3 .v3d-burb.es-pac { align-self: flex-start; background: var(--violeta-claro, #ede9ff); }
body.portal-v3 .v3d-burb__tx { display: block; white-space: pre-wrap; word-wrap: break-word; color: var(--tinta, #1e1a3f); }
body.portal-v3 .v3d-burb__h { display: block; font-size: .68rem; color: var(--v3d-meta); margin-top: 5px; }
body.portal-v3 .v3d-msj__adj { display: block; margin-top: 6px; font-size: .82rem; }
body.portal-v3 .v3d-msj__comp { margin-top: 12px; }
body.portal-v3 .v3d-msj__comp textarea {
  width: 100%;
  font: 400 .92rem/1.45 var(--fuente, Inter, sans-serif);
  color: var(--tinta, #1e1a3f);
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  border-radius: 12px;
  padding: 10px 12px;
  resize: vertical;
}
body.portal-v3 .v3d-msj__comp textarea:focus-visible { outline: 3px solid var(--violeta, #5b4fcf); outline-offset: 1px; }
body.portal-v3 .v3d-msj__acc { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
body.portal-v3 .v3d-msj__acc .btn--primario { margin-left: auto; }
body.portal-v3 .v3d-msj__cerrada { text-align: center; margin-top: 14px; }
body.portal-v3 .v3d-msg-nuevo { margin-left: auto; }

/* Badge de no leídos en el ítem del lateral (mockup index.html:41). */
body.portal-v3 .v3d-lat__badge {
  margin-left: auto;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--v3d-rojo, #b3261e);
  color: #fff;                          /* 6,54 */
  font-size: .72rem;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
}

/* ── FASE 62d-1 · Grilla semanal de la agenda ─────────────────────────────
   🔴 LOS CUATRO ESTADOS NO SE DISTINGUEN SOLO POR COLOR: cada celda lleva su
   propio TEXTO (Libre · Bloq. · iniciales/Cita · ·) y su aria-label. El color
   acompaña, no informa solo.
   🔴 CONTRASTES RECALCULADOS (verificar-fase62.py):
     · Libre    #0a6b52 sobre #e2f5f3 = 5,74
     · Bloq.    #605a7d sobre #f2f0fb = 5,9
     · Con cita #5b4fcf sobre #ede9ff = 5,11
     · Sin cupo: #605a7d con opacity .7 ⇒ efectivo #908ca4 = 3,24
       🔴 LA CIFRA ANTERIOR (6,45) ERA FALSA: ignoraba el opacity, y con .55 el
       contraste efectivo era 2,42. Se subió a .7 para pasar el 3:1 sin que deje
       de leerse como atenuada. Una cifra declarada que no se puede reproducir es
       peor que no declarar nada. */
body.portal-v3 .v3d-agnav { margin-left: auto; display: flex; gap: 12px; flex-wrap: wrap; }
body.portal-v3 .v3d-ag-aviso:empty { display: none; }
/* 63a: el mockup usa una grilla CSS con gap:6px (portal.css:366). En una
   <table> el equivalente exacto del hueco entre celdas es border-spacing. */
body.portal-v3 .v3d-grilla { width: 100%; border-collapse: separate; border-spacing: 6px; }
body.portal-v3 .v3d-grilla th {
  font: 600 .78rem/1.2 var(--fuente, Inter, sans-serif);
  color: var(--v3d-meta);
  text-align: center;
  padding: 4px 2px;
}
body.portal-v3 .v3d-grilla th small { display: block; font-size: .95rem; color: var(--tinta, #1e1a3f); }
body.portal-v3 .v3d-grilla__h {
  text-align: right !important;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* 44px: objetivo táctil recomendado. Con 12 filas la grilla mide ~570px y
   sigue entrando sin scroll horizontal en 1180 (7 columnas × 56px + horas). */
/* 🔴 FASE 63a — TODAS las celdas con la caja del mockup (portal.css:371-377):
   borde 1.5px, radio 10px y hover violeta. El estado se distingue por FONDO +
   TEXTO (los tres pares están declarados abajo y superan 4,5); el borde es
   refuerzo visual, no el portador del significado — por eso no se declara un
   contraste de borde que no cumpliría por sí solo. */
body.portal-v3 .v3d-cupo {
  text-align: center;
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  background: #fff;
  border-radius: 10px;
  font: 600 .78rem/1 var(--fuente, Inter, sans-serif);
  padding: 0;
  height: 46px;
  min-width: 56px;
  transition: border-color .12s, color .12s, background .12s;
}
body.portal-v3 .v3d-cupo:hover { border-color: var(--violeta, #5b4fcf); color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-cupo__b {
  width: 100%;
  height: 44px;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  border-radius: 9px;
}
body.portal-v3 .v3d-cupo__b:focus-visible { outline: 3px solid var(--violeta, #5b4fcf); outline-offset: 1px; }
body.portal-v3 .v3d-cupo.es-libre { background: var(--menta-claro, #e2f5f3); border-color: #a9dfd3; color: var(--v3d-verde); }   /* 5,74 */
body.portal-v3 .v3d-cupo.es-bloq { background: #f2f0f7; border-color: var(--linea, rgba(91, 79, 207, .14)); color: var(--v3d-meta); } /* 5,71 */
body.portal-v3 .v3d-cupo.es-cita { background: var(--violeta-claro, #ede9ff); border-color: #cdc6f2; color: var(--violeta, #5b4fcf); } /* 5,11 */
/* 63a: .es-nada ya no se pinta —no quedan celdas muertas—, pero la regla se
   deja por si un repintado viejo alcanza a montarse durante la carga. */
body.portal-v3 .v3d-cupo.es-nada { background: #fff; color: var(--v3d-meta); opacity: .7; cursor: default; }
body.portal-v3 .v3d-cupo__b[data-cupo-nuevo] { font-weight: 500; }
body.portal-v3 .v3d-leyenda--ag i.es-libre { background: var(--menta-claro, #e2f5f3); box-shadow: inset 0 0 0 1.5px var(--v3d-verde); }
body.portal-v3 .v3d-leyenda--ag i.es-bloq { background: #f2f0f7; box-shadow: inset 0 0 0 1.5px var(--v3d-meta); }
body.portal-v3 .v3d-leyenda--ag i.es-cita { background: var(--violeta-claro, #ede9ff); box-shadow: inset 0 0 0 1.5px var(--violeta, #5b4fcf); }
/* #cfcbe6 daba 1,57 sobre blanco: el cuadrito era invisible y la leyenda
   tenía una entrada que no se veía. #8b81d8 = 3,37 (el mismo tono que ya usa
   la serie MRS: no se inventa un color nuevo para esto). */
body.portal-v3 .v3d-leyenda--ag i.es-nada { background: #fff; box-shadow: inset 0 0 0 1.5px #8b81d8; }

/* ── FASE 65-B · Agenda en vista de MES ───────────────────────────────────
   Calendario mensual + detalle del día debajo + conmutador «Mes | Semana».

   🔴 TODOS los selectores de este bloque empiezan por `body.portal-v3` (es
   gate). En el portal CLÁSICO trabaja una médica real: una sola regla suelta
   la alcanzaría a ella, que no pidió nada y no debe ver cambiar un píxel.
   🔴 NO SE REUSAN .pc-cal/.pc-dia (el mini-calendario de la paciente): su
   paleta es de CONTROL y su semántica es la contraria —para la paciente una
   hora libre es buena noticia; para la médica un día sin reservas no lo es—.
   Se reusa la ESTRUCTURA (grid de 7 columnas con offset a lunes), no las
   clases. Tampoco se crea ninguna regla .ag-*: esas son del clásico.
   🔴 EL COLOR DEL DÍA REUSA LAS TRES CLASES DE LA GRILLA (es-libre / es-bloq /
   es-cita) con los MISMOS pares ya verificados, así que la leyenda es la
   misma en las dos vistas:
     · disponible  #0a6b52 sobre #e2f5f3 = 5,74
     · bloqueado   #605a7d sobre #f2f0f7 = 5,71
     · con cita    #5b4fcf sobre #ede9ff = 5,11
     · día pasado  #605a7d sobre #faf9fd = 6,15  (sin opacity: la 62d-1 ya nos
       enseñó que un contraste declarado sobre un opacity es un contraste falso)
     · conmutador activo #fff sobre #5b4fcf = 6,06
   El contador de cada día hereda el color del estado (`color: inherit`): no se
   declara un cuarto par que habría que volver a verificar. */

/* Conmutador de vista. Vive dentro de .v3d-agnav en LAS DOS vistas. */
body.portal-v3 .v3d-conm {
  display: inline-flex;
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  border-radius: 999px;
  padding: 2px;
  background: #fff;
  gap: 2px;
}
body.portal-v3 .v3d-conm__b {
  border: 0;
  background: none;
  border-radius: 999px;
  padding: 5px 14px;
  min-height: 30px;
  cursor: pointer;
  font: 600 .78rem/1 var(--fuente, Inter, sans-serif);
  color: var(--v3d-meta);
}
body.portal-v3 .v3d-conm__b:hover { color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-conm__b.is-on { background: var(--violeta, #5b4fcf); color: #fff; }  /* 6,06 */
body.portal-v3 .v3d-conm__b:focus-visible { outline: 3px solid var(--violeta, #5b4fcf); outline-offset: 1px; }

/* Calendario del mes. */
body.portal-v3 .v3d-mes { margin-top: 6px; }
body.portal-v3 .v3d-mes__dow,
body.portal-v3 .v3d-mes__grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;                              /* el mismo hueco de la grilla semanal */
}
body.portal-v3 .v3d-mes__dow {
  margin-bottom: 6px;
  font: 600 .78rem/1.2 var(--fuente, Inter, sans-serif);
  color: var(--v3d-meta);
  text-align: center;
}
body.portal-v3 .v3d-mesd {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 62px;
  padding: 4px 2px;
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  border-radius: 10px;
  background: #fff;
  cursor: pointer;
  font: 600 .82rem/1 var(--fuente, Inter, sans-serif);
  color: var(--tinta, #1e1a3f);
  transition: border-color .12s, color .12s, background .12s;
}
body.portal-v3 .v3d-mesd b { font-size: 1rem; font-weight: 700; }
/* El contador acompaña al color del estado: hereda, no declara un par nuevo. */
body.portal-v3 .v3d-mesd small { font-size: .7rem; font-weight: 600; color: inherit; }
body.portal-v3 .v3d-mesd:hover { border-color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-mesd:focus-visible { outline: 3px solid var(--violeta, #5b4fcf); outline-offset: 1px; }
body.portal-v3 .v3d-mesd.es-libre { background: var(--menta-claro, #e2f5f3); border-color: #a9dfd3; color: var(--v3d-verde); }   /* 5,74 */
body.portal-v3 .v3d-mesd.es-bloq { background: #f2f0f7; border-color: var(--linea, rgba(91, 79, 207, .14)); color: var(--v3d-meta); } /* 5,71 */
body.portal-v3 .v3d-mesd.es-cita { background: var(--violeta-claro, #ede9ff); border-color: #cdc6f2; color: var(--violeta, #5b4fcf); } /* 5,11 */
/* Hoy = ANILLO, no color de estado: si «hoy» pintara, taparía el dato real del
   día y habría que elegir entre saber qué día es y saber cómo está. */
body.portal-v3 .v3d-mesd.es-hoy { box-shadow: 0 0 0 2px var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-mesd.es-sel { border-width: 2.5px; border-color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-mesd.es-pasado {
  background: #faf9fd;
  border-color: var(--linea, rgba(91, 79, 207, .14));
  color: var(--v3d-meta);                /* 6,15 sobre #faf9fd */
  border-style: dashed;
}
/* Relleno antes del día 1: hueco, sin borde y sin nada que leer. */
body.portal-v3 .v3d-mesd.es-off { border: 0; background: none; min-height: 0; }

/* Detalle del día, DEBAJO del calendario. Vacío no ocupa sitio. */
body.portal-v3 .v3d-agdet:empty { display: none; }
body.portal-v3 .v3d-agdet__cupos { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
/* 🔑 El chip es el MISMO .v3d-cupo de la grilla (para que v3dAgCeldaSet lo
   sepa manejar sin una línea nueva), así que su maquetación cuelga del
   CONTENEDOR: cualquier clase extra en el chip la borraría el primer toggle,
   que reescribe className entero. */
body.portal-v3 .v3d-agdet__cupos .v3d-cupo {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 42px;
  min-width: 0;
  padding: 0 4px 0 10px;
  text-align: left;
}
body.portal-v3 .v3d-agdet__cupos .v3d-cupo__b {
  width: auto;
  height: 34px;
  padding: 0 10px;
  border-radius: 8px;
  background: rgba(255, 255, 255, .65);
}
body.portal-v3 .v3d-agdet__h { font-variant-numeric: tabular-nums; font-weight: 700; }
body.portal-v3 .v3d-agdet__t { padding: 0 6px; font-weight: 600; }
body.portal-v3 .v3d-agdet__tit {
  margin: 16px 0 6px;
  font: 700 .9rem/1.3 var(--fuente, Inter, sans-serif);
  color: var(--tinta, #1e1a3f);          /* 16,47 sobre blanco */
}
body.portal-v3 .v3d-agdet__pacs { display: flex; flex-direction: column; gap: 6px; }
body.portal-v3 .v3d-agdet__pac {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 8px 10px;
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  border-radius: 10px;
}
body.portal-v3 .v3d-agdet__pac .btn { margin-left: auto; }

/* Pantallas angostas: el calendario NO se reordena (un mes con menos de 7
   columnas deja de ser un mes); se le baja el alto y el número manda. */
@media (max-width: 720px) {
  body.portal-v3 .v3d-mesd { min-height: 52px; }
  body.portal-v3 .v3d-mesd small { font-size: .62rem; }
  body.portal-v3 .v3d-agdet__pac .btn { margin-left: 0; }
}

/* ── FASE 62e · shell: panel flotante, sugerencias, plegado, skip link, toasts ─
   Reusa .v3d-menu/.v3d-kmenu (el panel de la campana hereda su caja y su cierre),
   .v3d-contador, .v3d-lnk, .v3d-it__*, .v3d-chipic, .v3-sr.

   🔴 CONTRASTES RECALCULADOS:
     · glifo de marca: #fff sobre el degradado de marca (#5b4fcf→#7a6ee0) = 6,06
     · tecla ⌘K/Ctrl K: #605a7d sobre #f2f0fb = 5,90
     · toast: #fff sobre #1e1a3f = 16,47
     · skip link: #fff sobre #5b4fcf = 6,06
     · aviso "quieto" del panel: hereda .v3d-it__tx (#1e1a3f sobre #fff = 16,47) */

/* ── Glifo de la marca (mockup portal.css:43-46) ─────────────────────────── */
/* 🔴 FASE 63a — el chip pasa a BLANCO. El isotipo es morado (#7b2d8e) y
   naranjo (#ff9300): sobre el degradado violeta del lateral el morado
   desaparece (queda en ~1,6 de contraste contra #332c66) y la marca se lee
   como media figura naranja. Sobre blanco los dos colores rinden.
   El chip blanco sobre el lateral oscuro: 16,47 — no es texto, pero se declara
   igual porque es el elemento que delimita la marca. */
body.portal-v3 .v3-lat__glifo {
  width: 38px;
  height: 38px;
  border-radius: 12px;
  background: #fff;
  color: #fff;                          /* 6,06 · lo hereda el glifo SVG si vuelve */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  margin-right: 9px;
  overflow: hidden;
}
body.portal-v3 .v3-lat__glifo svg { width: 20px; height: 20px; }
body.portal-v3 .v3-lat__glifo img {
  width: 24px;
  height: 24px;
  object-fit: contain;                  /* el isotipo NO se deforma nunca */
  display: block;
}

/* ── Panel flotante de la campana ────────────────────────────────────────── */
body.portal-v3 .v3d-flot { min-width: 340px; max-width: 380px; max-height: 70vh; overflow: auto; padding: 4px; }
body.portal-v3 .v3d-flot__cab {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px 10px;
  border-bottom: 1px solid var(--linea, rgba(91, 79, 207, .14));
}
body.portal-v3 .v3d-flot__cab h3 { margin: 0; font-size: .95rem; font-weight: 600; }
body.portal-v3 .v3d-flot__it {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  font: inherit;
  padding: 10px;
  border-radius: var(--r-sm, 12px);
  cursor: pointer;
}
body.portal-v3 .v3d-flot__it.es-quieto { cursor: default; }
body.portal-v3 .v3d-flot__it:not(.es-quieto):hover { background: var(--violeta-claro, #ede9ff); }
body.portal-v3 .v3d-flot__it:not(.es-quieto):hover b { color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-flot__it:focus-visible { outline: 3px solid var(--violeta, #5b4fcf); outline-offset: -1px; }
body.portal-v3 .v3d-flot__it .v3d-chipic { width: 32px; height: 32px; border-radius: 9px; }
body.portal-v3 .v3d-flot__vacio { padding: 18px 12px; text-align: center; color: var(--v3d-meta); font-size: .88rem; }
body.portal-v3 .v3d-flot__pie { border-top: 1px solid var(--linea, rgba(91, 79, 207, .14)); padding: 9px 10px 4px; text-align: right; }

/* ── Sugerencias del buscador ────────────────────────────────────────────── */
body.portal-v3 .v3d-buscar { position: relative; }
body.portal-v3 .v3d-sug {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  background: #fff;
  border-radius: var(--r-sm, 12px);
  box-shadow: var(--sombra, 0 16px 40px rgba(91, 79, 207, .16));
  padding: 6px;
  z-index: 47;
  max-height: 60vh;
  overflow: auto;
}
body.portal-v3 .v3d-sug__grupo {
  font: 600 .72rem/1.2 var(--fuente, Inter, sans-serif);
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--v3d-meta);
  padding: 9px 10px 5px;
}
body.portal-v3 .v3d-sug__it {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  font: inherit;
  padding: 8px 10px;
  border-radius: 10px;
  cursor: pointer;
}
/* 🔴 B-6 — #faf9ff daba 1,05:1 contra el blanco: el hover era invisible.
   HONESTIDAD DE LA CIFRA: ningún violeta claro llega a 3:1 sin volverse un
   bloque oscuro (#ede9ff = 1,19). Un fondo de hover NO es un objeto que porte
   información —es un realce— y por eso el estado se refuerza donde el contraste
   sí se puede medir: el TEXTO pasa a violeta (#5b4fcf sobre #ede9ff = 5,11) y
   el foco sigue con su anillo de 3 px. Dos canales verificables en vez de un
   fondo que no se ve. */
body.portal-v3 .v3d-sug__it:hover { background: var(--violeta-claro, #ede9ff); }
body.portal-v3 .v3d-sug__it:hover b { color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-sug__it:focus-visible { outline: 3px solid var(--violeta, #5b4fcf); outline-offset: -1px; }
body.portal-v3 .v3d-sug__it b { display: block; font-size: .9rem; font-weight: 600; }
body.portal-v3 .v3d-sug__it .v3d-meta { display: block; font-size: .8rem; }
body.portal-v3 .v3d-sug__vacio { padding: 16px 12px; color: var(--v3d-meta); font-size: .87rem; }
/* 🔴 B-2 — SOLAPE DE 37 px. El botón "Buscar" es position:absolute (right:6px)
   y el kbd estaba en el flujo del flex: se montaban uno sobre otro en TODAS las
   vistas. El kbd se posiciona a la izquierda del botón y el input reserva el
   espacio de los dos. Bajo 1180 el kbd se oculta: el atajo sigue funcionando
   —es un recordatorio, no el control—. */
body.portal-v3 .v3d-kbd {
  position: absolute;
  right: 78px;
  font: 600 .72rem/1 var(--fuente, Inter, sans-serif);
  color: var(--v3d-meta);               /* 5,90 sobre #f2f0fb */
  background: #f2f0fb;
  border-radius: 6px;
  padding: 4px 6px;
  flex: none;
  white-space: nowrap;
  pointer-events: none;
}
@media (max-width: 1180px) { body.portal-v3 .v3d-kbd { display: none; } }

/* ── Skip link ───────────────────────────────────────────────────────────────
   🔴 JAMÁS display:none: lo sacaría del orden de tabulación y el enlace
   dejaría de existir para quien navega con teclado (mismo criterio que .v3-sr). */
body.portal-v3 .v3-salta {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 60;
  background: var(--violeta, #5b4fcf);
  color: #fff;                          /* 6,06 */
  font: 600 .9rem/1 var(--fuente, Inter, sans-serif);
  padding: 12px 16px;
  border-radius: 0 0 10px 0;
  text-decoration: none;
}
body.portal-v3 .v3-salta:focus {
  left: 0;
  outline: 3px solid #fff;
  outline-offset: -5px;
}

/* ── Toasts ──────────────────────────────────────────────────────────────── */
body.portal-v3 .v3d-toasts {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 70;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}
/* 🔴 62f — el toast se leía como texto suelto en el borde inferior. Se le da
   cuerpo de tarjeta (fondo blanco, borde y sombra como .v3d-card).
   CONTRASTES: texto #1e1a3f sobre #fff = 16,47 · check #0a6b52 sobre #fff = 5,74. */
body.portal-v3 .v3d-toast {
  display: flex;
  align-items: center;
  gap: 9px;
  background: #fff;
  border: 1px solid var(--linea, rgba(91, 79, 207, .14));
  color: var(--tinta, #1e1a3f);         /* 16,47 sobre #fff */
  font: 500 .88rem/1.35 var(--fuente, Inter, sans-serif);
  padding: 11px 15px;
  border-radius: 12px;
  box-shadow: var(--sombra, 0 16px 40px rgba(91, 79, 207, .16));
  max-width: 340px;
}
body.portal-v3 .v3d-toast svg { width: 16px; height: 16px; flex: none; color: var(--v3d-verde); }

/* ── Plegado del lateral ─────────────────────────────────────────────────────
   🔴 El botón y el estado SOLO existen ≥980px: bajo ese ancho el lateral ya se
   oculta entero y plegarlo no significaría nada. */
body.portal-v3 .v3-lat__plegar {
  margin-left: auto;
  width: 30px;
  height: 30px;
  border-radius: 9px;
  border: 0;
  background: none;
  color: #bdb7e6;                       /* 6,54 sobre #332c66 (pie del lateral) */
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
  flex: none;
}
body.portal-v3 .v3-lat__plegar:hover { background: rgba(255, 255, 255, .10); color: #fff; }
body.portal-v3 .v3-lat__plegar:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
@media (max-width: 979px) { body.portal-v3 .v3-lat__plegar { display: none; } }

@media (min-width: 980px) {
  /* 🔴 UNA variable mueve todo: lateral, topbar, franja, contenido y pie. */
  body.portal-v3.v3-plegado { --v3-lat-w: 72px; }
  /* Plegado: solo íconos. Los rótulos se ocultan con la técnica que SÍ deja el
     texto disponible para el lector (no display:none), y cada botón conserva su
     aria-label/title. */
  /* 🔴 B-5: `.v3-lat__logo span` se llevaba TAMBIÉN el glifo —lo único que debe
     quedar visible al plegar—. Ahora se oculta solo el texto, que además está
     envuelto para poder alcanzarlo. */
  /* 63a2: se quitó .v3d-lat__sub de esta lista — ya no existe ese nodo. La
     bajada nueva NO necesita entrada propia: vive DENTRO de .v3-lat__logo-tx,
     así que se oculta con su contenedor. */
  body.portal-v3.v3-plegado .v3-lat-it__tx,
  body.portal-v3.v3-plegado .v3-lat__logo-tx,
  body.portal-v3.v3-plegado .v3-lat__pie-tx {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  body.portal-v3.v3-plegado .v3-lat-it { justify-content: center; }
  body.portal-v3.v3-plegado .v3-lat__glifo { margin-right: 0; }
  body.portal-v3.v3-plegado .v3-lat__pie { flex-direction: column; gap: 8px; }
  body.portal-v3.v3-plegado .v3-lat__plegar { margin-left: 0; }
}

/* ── FASE 62f · pulido visual (dictamen de Josefa) ────────────────────────
   CONTRASTES declarados: número de tabla #1e1a3f sobre #fff = 16,47 ·
   iniciales del avatar #fff sobre el degradado de marca = 6,06. */

/* 8 · números de tabla: color explícito y cifras de ancho fijo (columnas que
   se comparan de un vistazo, sin heredar color de nadie). */
body.portal-v3 .v3d-tabla td.v3d-num {
  color: var(--tinta, #1e1a3f);
  font-variant-numeric: tabular-nums;
}

/* 8 · avatar del Perfil: cuando no hay foto, U.foto devuelve un <span> con las
   iniciales. Con la regla de la <img> quedaba la letra suelta, sin círculo. */
body.portal-v3 span.v3d-perfil__foto {
  background: var(--grad-primario, linear-gradient(135deg, #5b4fcf, #7a6ee0));
  color: #fff;                          /* 6,06 */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--fuente-titulo, "Poppins", sans-serif);
  font-weight: 600;
  font-size: 1.15rem;
}

/* 10 · glifo del hero (mockup portal.css:133-134). Decorativo: el gap lo separa
   del nombre sin depender del espacio literal del HTML. */
body.portal-v3 .v3d-hero__pac h3 { display: flex; align-items: center; gap: 8px; }
body.portal-v3 .v3d-genero { color: var(--violeta, #5b4fcf); font-size: 1rem; }

/* 4 · gráfico MRS de la Historia: alto del mockup (portal.css:437) y columnas
   que no se estiran cuando hay pocas evaluaciones. La base 0-44 no se toca. */
body.portal-v3 .v3d-barras--evo { height: 190px; justify-content: center; }
body.portal-v3 .v3d-barras--evo > div { max-width: 96px; }

/* 5 · el panel de la campana se lee como capa sobre el contenido. */
body.portal-v3 .v3d-flot {
  right: 16px;
  box-shadow: 0 24px 60px rgba(30, 26, 63, .28);
}
body.portal-v3 .es-atenuada { opacity: .45; transition: opacity .15s; }

/* 6 · plegado fiel al contrato del mockup (portal.css:69): el badge de
   Mensajes no cabe en 72 px y se superpondría al ícono. */
@media (min-width: 980px) {
  body.portal-v3.v3-plegado .v3d-lat__badge { display: none; }

  /* 3 · FAB de Ayuda en modo icono dentro de los 72 px del lateral plegado.
     🔴 Scoped al estado plegado y por id: soporte-fab.js NO se toca. */
  body.portal-v3.v3-plegado #cd-soporte-fab {
    width: 44px;
    min-width: 44px;
    height: 44px;
    padding: 0;
    left: 14px;                         /* (72 - 44) / 2 = 14 */
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
  }
  /* El rótulo "Ayuda" se oculta con el patrón que SÍ deja el texto disponible
     para el lector (nunca display:none, que lo borraría del árbol). */
  body.portal-v3.v3-plegado #cd-soporte-fab span:not(.v3-sr) {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  body.portal-v3.v3-plegado #cd-soporte-fab svg { flex: none; }
}

/* ── FASE 63a · logo oficial + agenda fiel al mockup ──────────────────────
   CONTRASTES declarados (recalculados en verificar-fase62.py, no copiados):
     · bajada #a49ecf sobre #1e1a3f = 6,57 · sobre #332c66 (peor caso) = 4,94
     · "Fem" #ff9d8a sobre #1e1a3f = 8,19 · sobre #332c66 = 6,17
     · celda bloqueada: --v3d-meta sobre #f2f0f7 = 5,71
     · ISOTIPO — 🔴 63a2: las dos cifras que declaré antes (#7b2d8e) salían de
       un hex SUPUESTO, no medido. El PNG tiene exactamente dos colores, ahora
       muestreados del archivo: #772b95 (54.049 px) y #ff9300 (54.948 px).
         morado #772b95 : 8,19 sobre el chip blanco · 2,01 sobre #1e1a3f y
                          1,51 sobre #332c66  <- lo que descartó el isotipo
                                                 suelto sobre el lateral
         naranjo #ff9300: 2,23 sobre el chip blanco · 5,57 sobre el lateral
       Renata midió 2,72 y 6,06 sobre la captura renderizada: a 24 px el
       antialiasing mezcla el trazo con su fondo, así que su píxel no es el
       color puro. NO se copian sus cifras ni se pisan las mías: las dos son
       ciertas de cosas distintas (archivo vs. render). Queda para que ella
       zanje cuál se declara como oficial.
       LA DECISIÓN SE SOSTIENE CON CUALQUIERA DE LAS DOS: el morado sobre el
       lateral queda bajo 3,0 en ambas mediciones. Y ojo: sobre el chip blanco
       el que baja es el NARANJO (2,23) — como logotipo no le aplica el umbral
       de texto ni de control, y las dos mitades se distinguen entre sí; se
       declara igual para no fingir un cumplimiento que no corresponde. */

/* FASE 63b — avatares con color por paciente. Ver la nota de contraste sobre
   .v3d-ava--t0..t4: los cinco degradados del mockup se ajustaron porque
   ninguno llegaba a 4,5 con texto blanco. */

/* ══════════════════════════════════════════════════════════════════════════════
   FASE 63c · VISTA "FICHA DE LA PACIENTE" (v3d-ficha)
   Réplica de la ficha del mockup (vistas.js:746-1005) sobre las piezas que ya
   viven en producción: .v3d-card, .v3d-fichacab, .v3d-parcs, .v3d-vacio, .v3d-pill.
   Nada de esto reemplaza a .hv-* (hoja de vida clásica) ni al modal de ficha:
   son superficies distintas y las dos siguen intactas.
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── Pestañas (mockup .pestanas/.pest) ────────────────────────────────────── */
body.portal-v3 .v3d-pestanas {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  border-bottom: 1.5px solid var(--linea, #ece7f5);
  margin: 18px 0 0;
}
body.portal-v3 .v3d-pest {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  font: 600 .92rem/1.2 var(--fuente, Inter, sans-serif);
  color: var(--v3d-meta);
  padding: 12px 16px;
  margin-bottom: -1.5px;
  cursor: pointer;
  border-radius: 10px 10px 0 0;
}
body.portal-v3 .v3d-pest:hover { color: var(--violeta, #5b4fcf); background: #fbfaff; }
body.portal-v3 .v3d-pest.is-on {
  color: var(--violeta, #5b4fcf);
  background: var(--violeta-claro, #ede9ff);
  border-bottom-color: var(--violeta, #5b4fcf);
}
body.portal-v3 .v3d-pest:focus-visible { outline: 3px solid var(--violeta, #5b4fcf); outline-offset: 2px; }
body.portal-v3 .v3d-pest .v3d-ic { flex: none; }

/* ── Malla de la ficha ─────────────────────────────────────────────────────
   🔴 NO se copia el `1fr / 350px` fijo del mockup. El viewport REAL de Mauricio
   es 1536 (pantalla escalada al 125%): con la malla fija, la columna derecha le
   cortaba tarjetas. Se reusa la escalera de la 58d/e — una columna cuando ya no
   caben dos, y nunca a mitad de una tarjeta. */
body.portal-v3 .v3d-fichamalla {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(300px, 350px);
  gap: 18px;
  align-items: start;
  margin-top: 16px;
}
body.portal-v3 .v3d-fichamalla > .col { min-width: 0; display: flex; flex-direction: column; gap: 18px; }
@media (max-width: 1180px) {
  body.portal-v3 .v3d-fichamalla { grid-template-columns: minmax(0, 1fr); }
}

/* ── Motivo (mockup .motivo) ───────────────────────────────────────────────── */
body.portal-v3 .v3d-ficmotivo {
  background: var(--violeta-claro, #ede9ff);
  border-radius: var(--r-sm, 12px);
  padding: 14px 16px;
  margin-top: 14px;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
body.portal-v3 .v3d-ficmotivo small {
  display: block;
  font-size: .82rem;
  color: var(--violeta, #5b4fcf);
  font-weight: 600;
}
body.portal-v3 .v3d-ficmotivo b { font-size: .95rem; font-weight: 500; overflow-wrap: anywhere; }
body.portal-v3 .v3d-ficmotivo .v3d-meta { margin-left: auto; }

/* ── Nota clínica y sus secciones (mockup .nota-sec) ──────────────────────── */
body.portal-v3 .v3d-ficnota {
  border: 1.5px solid var(--linea, #ece7f5);
  border-radius: var(--r-sm, 12px);
  padding: 16px 18px;
  margin-top: 16px;
}
body.portal-v3 .v3d-ficnota__cab {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}
body.portal-v3 .v3d-ficnota__cab h3 { color: var(--violeta, #5b4fcf); font-size: 1rem; margin: 0; }
body.portal-v3 .v3d-ficnota__cab .v3d-meta { margin-left: auto; }
body.portal-v3 .v3d-notasec { margin-top: 16px; }
body.portal-v3 .v3d-notasec h4 {
  display: flex;
  align-items: center;
  gap: 9px;
  color: var(--violeta, #5b4fcf);
  font-size: .92rem;
  margin: 0 0 4px;
}
body.portal-v3 .v3d-notasec h4 .v3d-chipic { width: 26px; height: 26px; border-radius: 8px; }
body.portal-v3 .v3d-notasec p {
  margin: 0 0 0 35px;
  font-size: .93rem;
  color: var(--v3d-meta);
  overflow-wrap: anywhere;
}

/* ── Bloque privado (mockup .privado) ─────────────────────────────────────── */
body.portal-v3 .v3d-privado {
  background: #f7f5fb;
  border: 1px solid #e6e0f0;
  border-radius: var(--r-sm, 12px);
  padding: 12px 14px;
  margin-top: 18px;
}
body.portal-v3 .v3d-privado > b { font-size: .88rem; display: inline-flex; align-items: center; gap: 6px; }
body.portal-v3 .v3d-privado p { margin: 6px 0 0; overflow-wrap: anywhere; }
body.portal-v3 .v3d-privado__rot { font-weight: 700; font-size: .84rem; color: var(--v3d-tinta, #2b2440); }

/* ── Mini-tabla (mockup .mini-tabla) ──────────────────────────────────────── */
body.portal-v3 .v3d-minitabla { width: 100%; border-collapse: collapse; margin-top: 8px; }
body.portal-v3 .v3d-minitabla td {
  padding: 9px 0;
  border-bottom: 1px solid var(--linea, #ece7f5);
  font-size: .88rem;
  color: var(--v3d-meta);
  vertical-align: top;
}
body.portal-v3 .v3d-minitabla td:last-child {
  text-align: right;
  font-weight: 600;
  color: var(--v3d-tinta, #2b2440);
  overflow-wrap: break-word;
}
body.portal-v3 .v3d-minitabla tr:last-child td { border-bottom: 0; }

/* ── Pares de antecedentes ─────────────────────────────────────────────────
   🔴 La rejilla y la tarjeta de par YA EXISTEN desde la 62c y NO se redeclaran.
   Mi primera versión las copió, y su `overflow-wrap: anywhere` PISÓ el arreglo D5
   de Renata (break-word): el correo volvía a cortarse a mitad de palabra en toda
   la 62c, no solo acá. Lo cazó el gate permanente F62c3-D5.
   Regla que queda: si la clase existe, se usa; no se clona.
   🔴 Y este comentario NO nombra el selector con su forma exacta: el gate lo
   CUENTA en el archivo, así que citarlo aquí lo haría fallar por mi explicación.
   Es la trampa de "los comentarios que citan tokens", ya pagada tres veces. */

/* Lo único NUEVO: un valor ausente no se lee como un dato escrito por la médica. */
body.portal-v3 .v3d-par.es-vacio b { color: var(--v3d-meta); font-style: italic; }

/* Y la rejilla de "Acciones rápidas": mismos .v3d-par, pero como botones y en
   tantas columnas como quepan (son 4 y el mockup las quiere parejas). */
body.portal-v3 .v3d-pares--acc { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
body.portal-v3 .v3d-paracc {
  background: #fbfaff;
  cursor: pointer;
  text-align: center;
  font: inherit;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}
body.portal-v3 .v3d-paracc .v3d-ic { color: var(--violeta, #5b4fcf); }
body.portal-v3 .v3d-paracc b { font-size: .88rem; font-weight: 600; color: var(--v3d-tinta, #2b2440); }
body.portal-v3 .v3d-paracc:hover { border-color: #cdc6f2; background: var(--violeta-claro, #ede9ff); }

/* ── Documentos de la consulta ────────────────────────────────────────────── */
body.portal-v3 .v3d-ficdoc {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--linea, #ece7f5);
}
body.portal-v3 .v3d-ficdoc:last-child { border-bottom: 0; }
body.portal-v3 .v3d-ficdoc > span { flex: 1; min-width: 0; overflow-wrap: break-word; }
body.portal-v3 .v3d-ficdoc .v3d-pill { flex: none; }

/* ── Varios ───────────────────────────────────────────────────────────────── */
body.portal-v3 .v3d-ficrefiere { font-size: .93rem; overflow-wrap: anywhere; margin: 0 0 6px; }
body.portal-v3 .v3d-ficalcance { border-top: 1px dashed var(--linea, #ece7f5); padding-top: 12px; margin-top: 14px; }
body.portal-v3 .v3d-ficancho { width: 100%; margin-top: 10px; }
body.portal-v3 .v3d-fichacab .v3d-menu { text-align: left; }

/* 🔴 LA RONDA DE JOSEFA (anillo + extremo claro del avatar propio) NO va acá.
   Se aplica EN SU SITIO, sobre la regla #v3d-quien-av que ya existe más arriba
   (la que nació en la 63b). Dejar acá un segundo bloque para el mismo selector
   funcionaría por cascada y sería exactamente la trampa que paga el próximo que
   lea el archivo: dos reglas para lo mismo, una escondida 1.400 líneas después.
   Un selector, un lugar. */

/* ── FASE 63d · lista de consultas de prueba ──────────────────────────────
   Reusa .v3d-card, .v3d-meta, .btn y la píldora `aviso` ya declarada
   (#6b5200 sobre #fbf0da = 6,56 · recalculado en verificar-fase62.py). */
body.portal-v3 .v3d-demo__n { margin-top: 12px; }
body.portal-v3 .v3d-demo__lista { list-style: none; margin: 6px 0 0; padding: 0; }
body.portal-v3 .v3d-demo__lista li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 0;
  border-top: 1px solid var(--linea, rgba(91, 79, 207, .14));
  font-size: .88rem;
}

/* ── FASE 64a · «Nuevo mensaje» en la vista v3 ────────────────────────────
   Reusa .v3d-card, .v3d-card__cab, .v3d-campo, .v3d-sel, .v3d-msj__acc,
   .v3d-file, .btn y .v3d-meta. Lo único que falta es que el <select> y el
   <textarea> del formulario tomen la caja de .v3d-campo input (la regla
   existente solo alcanza a input) y el aire del pie. */
body.portal-v3 .v3d-campo select,
body.portal-v3 .v3d-campo textarea {
  width: 100%;
  font: 400 .95rem/1.35 var(--fuente, Inter, sans-serif);
  color: var(--tinta, #1e1a3f);
  background: #fff;
  border: 1.5px solid var(--linea, rgba(91, 79, 207, .14));
  border-radius: 10px;
  padding: 10px 12px;
}
body.portal-v3 .v3d-campo textarea { resize: vertical; min-height: 92px; }
body.portal-v3 .v3d-campo select:focus-visible,
body.portal-v3 .v3d-campo textarea:focus-visible {
  outline: 3px solid var(--violeta, #5b4fcf);
  outline-offset: 1px;
}
body.portal-v3 .v3d-nm__pie { margin-top: 10px; }

/* ══════════════════════════════════════════════════════════════════════════════
   FASE 64b · EL EDITOR DE FICHA, EN PÁGINA
   La ventana emergente desaparece y su contenido vive dentro de v3d-ficha.

   🔴 NO SE REDECLARA NADA DE LO QUE EL EDITOR YA USA: .campo (estilos.css),
   .fc-tramite / .fc-g (portal-v3.css), .mrs-panel (estilos.css) y los estilos en
   línea del markup mudado siguen sirviendo tal cual — ninguno estaba scopeado
   bajo .modal, y por eso el editor conserva su aspecto fuera de él.
   Lo único que cambia es el CONTENEDOR: el modal daba 560px de ancho; acá hay
   toda la columna, así que hay que ponerle un techo de lectura a los campos.
   ══════════════════════════════════════════════════════════════════════════════ */

body.portal-v3 .v3d-fic-editor {
  margin-bottom: 18px;
  scroll-margin-top: calc(var(--v3d-top-h, 74px) + 12px);
}

/* ── MEASURE · UN SOLO BORDE DERECHO (dictamen de Josefa, F64b2) ───────────
   🔴 BLOQUEANTE que arreglé mal la primera vez. Le puse techo SOLO a los campos,
   así que los textareas cortaban en x≈1175 mientras los párrafos de
   ayuda y las cajas tintadas seguían hasta x≈1483: dos bordes derechos distintos
   en la misma tarjeta. Josefa: «es lo primero que se ve y es lo que hace parecer
   injerto». Y no era solo estética: esas líneas llegaban a ~150 caracteres,
   cuando en el popup wrappeaban a ~75 — al doble de measure se pierde el renglón
   al volver, que es exactamente lo que uno hace leyendo una nota clínica.
   El techo se pone AL CONTENEDOR, no campo por campo: así TODO lo que cuelga del
   cuerpo del editor (cajas tintadas, panel MRS, panel de transcripción, los
   cuatro grupos, los avisos y la fila de botones) comparte UN solo borde. */
body.portal-v3 .v3d-fic-editor #mf-tab-consulta > * {
  max-width: 78ch;
  width: 100%;
}
/* Y los campos, explícito: heredan el ancho del grupo, pero si alguien mueve un
   textarea fuera de .fc-grupos no queda sin techo. */
body.portal-v3 .v3d-fic-editor .campo textarea,
body.portal-v3 .v3d-fic-editor .campo input {
  max-width: 78ch;
  width: 100%;
}

/* Los cuatro grupos por destino y el campo suelto de acuerdos: mismo aire que
   tenían dentro del modal, ahora sin el padding del .modal. */
body.portal-v3 .v3d-fic-editor .fc-grupos { margin-top: 6px; }
body.portal-v3 .v3d-fic-editor .mf-suelto { margin: 14px 0; }

/* El pie: pegajoso al fondo del editor para que Guardar y Firmar no se pierdan
   tras diez campos de scroll. El modal los tenía siempre a la vista. */
body.portal-v3 .v3d-fic-editorpie {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 18px;
  padding: 12px 0 2px;
  /* 🔴 FONDO SÓLIDO (Josefa, F64b2). Lo había puesto en degradado y el 22%
     superior quedaba translúcido: el texto que scrollea por debajo se leía a
     través del pie, justo encima de Guardar y Firmar. Un pie de acciones tiene
     que ser opaco o no es un pie: es una marca de agua. */
  background: #fff;
  border-top: 1px solid var(--linea, #ece7f5);
}
body.portal-v3 .v3d-fic-editorpie__acc {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* MODO EDICIÓN: con el editor abierto, Resumen deja de ser dos columnas. El
   resto de Resumen NO se oculta: queda debajo, para poder mirar los documentos
   de la consulta mientras se escribe la receta. */
body.portal-v3 .v3d-fichamalla.es-editando { grid-template-columns: minmax(0, 1fr); }

/* ── FICHA FIRMADA · se VE que no se puede escribir ────────────────────────
   El acto médico está cerrado. Antes los campos se veían idénticos a los de un
   borrador y la médica solo se enteraba al guardar (409). */
body.portal-v3 .v3d-fic-editor.es-firmada .campo textarea,
body.portal-v3 .v3d-fic-editor.es-firmada .campo input {
  background: #f7f5fb;
  border-style: dashed;
  color: var(--v3d-meta);
  cursor: default;
}

/* ══════════════════════════════════════════════════════════════════════════════
   FASE 64c · PULIDO DEL EDITOR EN PÁGINA (dictamen firmado de Josefa)
   ══════════════════════════════════════════════════════════════════════════════ */

/* ── P1 · LA TARJETA DE LECTURA NO COMPITE CON EL EDITOR ───────────────────
   🔴 Hallazgo de Josefa, y es clínico antes que estético: con el editor abierto,
   la tarjeta «Ficha clínica» sigue mostrando la nota de la última consulta
   FIRMADA (viene de hoja_vida, que solo entrega firmadas) mientras el editor
   muestra la consulta que se está escribiendo. Dos versiones del mismo campo,
   a la vez, en la misma pantalla: la médica puede leer la vieja creyendo que es
   la que tiene en el teclado. Se atenúa y se pliega mientras se edita. */
body.portal-v3 .v3d-fichamalla.es-editando .v3d-fic-lectura {
  opacity: .55;
  filter: saturate(.85);
}
body.portal-v3 .v3d-fichamalla.es-editando .v3d-fic-lectura:hover,
body.portal-v3 .v3d-fichamalla.es-editando .v3d-fic-lectura:focus-within {
  opacity: 1;
  filter: none;
}
body.portal-v3 .v3d-fic-lectura__aviso {
  display: none;
  align-items: center;
  gap: 8px;
  font: 600 .82rem/1.3 var(--fuente, Inter, sans-serif);
  color: var(--v3d-meta);
  background: #f7f5fb;
  border: 1px dashed var(--linea, #ece7f5);
  border-radius: 10px;
  padding: 8px 12px;
  margin-bottom: 10px;
}
body.portal-v3 .v3d-fichamalla.es-editando .v3d-fic-lectura__aviso { display: flex; }
/* El cuerpo de la tarjeta se pliega: queda el encabezado como referencia. */
body.portal-v3 .v3d-fichamalla.es-editando .v3d-fic-lectura__cuerpo { display: none; }

/* ── P2 · CHIP DE ESTADO + CERRAR EN LA CABECERA ───────────────────────────
   En una ficha firmada el punteado de los campos es imperceptible de un vistazo:
   el estado tiene que estar donde se mira primero, y con TEXTO (no solo color:
   quien no percibe color leía una ficha cerrada como si fuera un borrador). */
body.portal-v3 .v3d-fic-estado {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font: 700 .74rem/1 var(--fuente, Inter, sans-serif);
  text-transform: uppercase;
  letter-spacing: .04em;
  padding: 6px 11px;
  border-radius: 999px;
}
body.portal-v3 .v3d-fic-estado--borrador { background: var(--violeta-claro, #ede9ff); color: #4a3fb5; }
body.portal-v3 .v3d-fic-estado--firmada { background: #e2f5f3; color: #0b7266; }
body.portal-v3 .v3d-fic-editor .v3d-card__cab { flex-wrap: wrap; gap: 10px; }
body.portal-v3 .v3d-fic-cerrarcab { margin-left: auto; }

/* ── P3 · JERARQUÍA DEL MENÚ DE EMISIÓN ────────────────────────────────────
   🔴 Estaba invertida: la acción EXCEPCIONAL («…aparte, folio nuevo») se veía
   más fuerte que la habitual. Fila 1 rellena, fila 2 delineada. Y la
   advertencia va en TEXTO, no solo en el color del borde. */
body.portal-v3 .v3d-fic-docs {
  border-top: 1px solid var(--linea, #ece7f5);
  margin-top: 18px;
  padding-top: 14px;
}
body.portal-v3 .v3d-fic-docs h4 {
  font: 700 .82rem/1.2 var(--fuente, Inter, sans-serif);
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--v3d-meta);
  margin: 0 0 10px;
}
body.portal-v3 .v3d-fic-docs__aviso {
  font: .8rem/1.4 var(--fuente, Inter, sans-serif);
  color: var(--v3d-meta);
  margin: 8px 0 0;
}
/* fila 2 (los «aparte»): delineados, no rellenos
   🔴 FASE 64c2 · D-2 · LA REGLA ESTABA, PERO NO PINTABA NADA.
   Arriba (línea ~34) los mismos ids se pintan CON !important. Esta regla iba
   sin él: misma especificidad, más abajo… y perdía igual, porque !important
   gana antes de que el orden del archivo llegue a importar. El resultado era
   una jerarquía declarada en el CSS y ausente en la pantalla — el gate de
   texto la leía verde y la médica seguía viendo los dos botones idénticos.
   Se pone !important en las TRES declaraciones que tienen que ganarle a la
   regla de relleno. Empatan en !important, y ahí sí manda el orden: esta va
   después. */
body.portal-v3 #mf-emitir-rx-add,
body.portal-v3 #mf-emitir-or-add {
  background: transparent !important;
  border: 1.5px solid var(--linea, #ece7f5) !important;
  color: var(--v3d-meta) !important;
}
/* 🔴 Y EL HOVER TAMBIÉN ERA DE ACCIÓN PRIMARIA (línea ~62: brillo + elevación +
   sombra). Un botón delineado que al pasar el mouse se levanta y proyecta
   sombra vuelve a leerse como la acción habitual justo en el momento en que la
   persona está por hacer clic: la jerarquía se caía precisamente donde se
   decide. La fila 2 pasa a hover de contorno: sin relleno, sin brillo, sin
   elevación. El cambio de estado se declara en el borde y el texto, que pasan
   a violeta (6,06:1 sobre blanco, medido; el #cdc6f2 anterior daba 1,62:1 y
   como única señal de hover era invisible).
   border-color y color NECESITAN !important: la regla de arriba pone
   `border`/`color` con !important y aplica también en :hover. */
body.portal-v3 #mf-emitir-rx-add:hover,
body.portal-v3 #mf-emitir-or-add:hover {
  background: transparent !important;
  border-color: var(--violeta, #5b4fcf) !important;
  color: var(--violeta, #5b4fcf) !important;
  filter: none;
  transform: none;
  box-shadow: none;
}

/* ── P4 · EL PIE, ALINEADO AL MEASURE DEL CUERPO ───────────────────────────
   El pie llegaba a 1471 mientras el cuerpo cortaba en 1061: la única línea que
   no respetaba el borde común era justo la de las acciones. */
body.portal-v3 .v3d-fic-editorpie { max-width: 78ch; }

/* ── P5 · LA ESCALA MRS DEJA DE ESTIRARSE ──────────────────────────────────
   La etiqueta de cada dominio caía en una columna de ~90px y se leía en
   vertical, una palabra por línea (1:11). */
/* 🔴 FASE 64c2 · D-3 · DOS ERRORES ENCIMA DEL MISMO BLOQUE.
   1) Los selectores no existían. El markup real (mrsResultadosHtml) escribe
      .mrs-dominio / .mrs-dom-nom / .mrs-bar / .mrs-dom-val; la regla apuntaba
      a .mrs-dom, .mrs-barra y .sub-esc .fila span, que no aparecen en ningún
      lado. CSS no avisa de un selector que no matchea: se veía prolijo y no
      tocaba un solo píxel.
   2) Y con los nombres corregidos TAMPOCO habría alcanzado: la fila es un
      grid `90px 1fr 46px` (estilos.css:966). Un min-width en el ítem no
      ensancha un track fijo — lo DESBORDA. Lo que hay que mover es el track.
   Se hace scoped al editor en página (#mf-tab-consulta, dentro de
   .v3d-fic-editor) y por CSS puro: estilos.css NO se toca (su verificación de
   sitio completo ya está hecha) y el markup de mrsResultadosHtml tampoco, que
   lo comparten la sala, el modal y la hoja de vida.
   Medida: etiqueta 180px (antes 90 → una palabra por línea), riel con techo
   420px (antes 607 → 6,7:1 contra la etiqueta). */
body.portal-v3 .v3d-fic-editor #mf-tab-consulta .mrs-dominio {
  grid-template-columns: 180px 1fr 46px;
}
body.portal-v3 .v3d-fic-editor #mf-tab-consulta .mrs-dom-nom { min-width: 180px; }
body.portal-v3 .v3d-fic-editor #mf-tab-consulta .mrs-bar { max-width: 420px; }

/* ── P6 · TARJETAS TINTADAS → BLANCO CON REGLA IZQUIERDA ───────────────────
   Cuatro colores pastel compitiendo dentro de una tarjeta que ya es una unidad.
   El significado se conserva en la REGLA (3px) y en el texto, no en el fondo. */
body.portal-v3 .v3d-fic-editor #mf-tab-consulta > div[style] {
  background: #fff !important;
  border: 1px solid var(--linea, #ece7f5) !important;
  border-left: 3px solid var(--violeta, #5b4fcf) !important;
}
body.portal-v3 .v3d-fic-editor #mrs-panel {
  background: #fff;
  border: 1px solid var(--linea, #ece7f5);
  border-left: 3px solid var(--v3d-azul, #1f6ba8);
}
body.portal-v3 .v3d-fic-editor #tr-panel {
  background: #fff !important;
  border: 1px solid var(--linea, #ece7f5) !important;
  border-left: 3px solid #5b2a86 !important;
}

/* ── P6b · LAS OTRAS CUATRO PESTAÑAS SE ATENÚAN CON EL EDITOR ABIERTO ──────
   No se desactivan (la médica puede querer irse): se atenúan para que el foco
   quede donde está escribiendo. */
body.portal-v3 .v3d-pestanas.es-editando .v3d-pest:not(.is-on) { opacity: .45; }
body.portal-v3 .v3d-pestanas.es-editando .v3d-pest:not(.is-on):hover,
body.portal-v3 .v3d-pestanas.es-editando .v3d-pest:not(.is-on):focus-visible { opacity: 1; }
/* ══════════════════════════════════════════════════════════════════════════
   §66g · EL GRUPO DE LA TRANSCRIPCIÓN EN EL TOPBAR DE LA SALA
   Se AÑADE al final de portal-v3.css. No modifica ninguna regla existente.

   Problema: #v3-trx-btn ("Detener / Reanudar la transcripción"), #v3-trx-ia
   ("Ir preparando el borrador") y su leyenda #v3-trx-ia-est eran hijos SUELTOS
   de #v3-topbar, que es flex con flex-wrap (portal-v3.css:696-710). Al envolver,
   cada uno caía donde alcanzaba y el borrador quedaba lejos del botón al que
   pertenece. Además ninguno tenía regla propia: la leyenda estiraba la barra a
   antojo (medido: 68 px a 1536 · 124 px a 1180 · 193 px angosto).

   Ahora el JS los mete en #v3-trx-grupo: UN solo ítem del flex de la barra.
   ══════════════════════════════════════════════════════════════════════════ */
body.portal-v3 #v3-trx-grupo {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  min-width: 0;
  /* ⚠️ DECISIÓN DECLARADA. La primera versión reservaba altura fija
     (min-height:62px) para que la barra no creciera al aparecer el borrador.
     MEDIDO: empeoraba el reposo (68 → 83 px a 1536) y NO lograba altura estable
     (168 px con la leyenda), porque el texto es de largo variable. Se retira.
     Se puede retirar sin riesgo porque el parpadeo YA no depende de esto: con
     v3fPuedeVolver() la ida y la vuelta son incompatibles por construcción, así
     que una barra que cambia de altura ya no puede realimentar el ciclo. Queda
     como asunto de pulido visual para Josefa, no de corrección. */
}

/* ⚠️ La leyenda FLUYE junto a los botones, sin renglón propio. Se probó con
   `flex:1 0 100%` (renglón exclusivo) y MEDIDO empeoraba la barra: 126 px hoy
   contra 168 px con el renglón forzado, a 1536. La regla que no mejora una
   medición no entra. */
body.portal-v3 #v3-trx-ia-est {
  margin: 0;
  font: 500 .8rem/1.35 Inter, sans-serif;
  color: var(--v3-meta, #605a7d);
  max-width: 42ch;
}

/* El botón del borrador comparte la altura de golpeo del de transcripción: son
   dos controles hermanos y en un táctil se pulsan igual. */
body.portal-v3 #v3-trx-ia { min-height: 36px; }

/* ── §F69 · LA COLUMNA DERECHA: TODO CERRADO, UNA SOLA SECCIÓN ABIERTA ─────
   Mockup aprobado por Mauricio (31-jul): «prefiero la sección donde está todo
   cerrado, ahí se ve más ordenado».

   🟢 POR QUÉ ESTO NO PUEDE MOVER EL VIDEO
   El alto de la columna NO cambia: `.sesion-panel` ya vale `height:74vh` fijo
   (§1.4, más arriba, que esta fase NO toca). Abrir o cerrar una sección
   reparte el espacio DENTRO del scroller; la fila del grid `.sesion-sala` no
   se entera y `.sesion-video` no se mueve un píxel. Es la misma razón por la
   que el mockup pone `overflow:hidden` en la columna: nada puede empujar
   hacia afuera. (Se midió el rect del video en los tres estados igual: la
   regla de la casa es verificar, no confiar.)

   Todo va colgado de `.modo-jitsi`, como el §1.4: fuera de la sala esta hoja
   no cambia nada, y el portal clásico de Margot no ve ni una regla. */

/* 🔴 POR QUÉ TODO EL REPARTO VA DENTRO DE @supports
   Chromium interpone una caja propia, `::details-content`, entre el <details>
   y sus hijos. Sin estilarla, el <details> quedaba en 257 px y su cuerpo en
   1002: el contenido DESBORDABA y, con la columna en overflow:hidden, quedaba
   recortado y sin forma de llegar a él. Recortar sin scroll es peor que el
   hueco que veníamos a arreglar.
   Por eso el modo "una sección llena la columna" se aplica SOLO donde el
   navegador entiende `::details-content`. Donde no, no pasa nada: la columna
   sigue con su scroll de siempre y no se rompe nada. Degradar es barato;
   esconderle documentos a la médica, no. */
@supports selector(::details-content) {

/* 🔴 EL PRESUPUESTO DE ALTO — aritmética rehecha DOS veces, con la tarjeta de
   la paciente en su peor caso real (167,8 px, medido por Renata; la primera
   vuelta usó 83 y la segunda 141, las dos de una réplica pobre en datos):
       tarjeta entera ................. 167,80 px
       tarjeta compactada (1 línea) .... 76,39 px
       5 cabeceras (68,9 c/u) ......... 344,50 px
       6 huecos ....................... 96,0 / 60,0 px
       Sobre (mínimo) .................. 32,00 px
       ──────────────────────────────────────────
       costo fijo SIN compactar ....... 473,25 px

   🔴 POR QUÉ EL UMBRAL ES 950 Y NO 800
   El @media mira el VIEWPORT, no la pantalla, y Chrome se come ~95 px de
   barras. Con 800, la superficie libre para escribir una receta —descontando
   la cabecera interna (52) y el pie sticky (49)— quedaba en 24 px. Hoy en
   producción son 605. Autorizar 24 px para recetar es indefendible.
   Medido: a 800 → 24 px · a 864 → 68 · a 950 → 130 · a 1007 → 169.
   Y como un notebook de 1536×864 ya daba viewport ≈769, con 800 tampoco lo
   estaba recibiendo: subir a 950 no se lo quita a casi nadie, corrige a quién
   le llegaba. */
@media (min-height: 950px) {

/* 🔴 «DOCUMENTOS DE LA CONSULTA» NO ENTRA AL REPARTO DE ALTO.
   Todo lo de acá abajo cuelga de `.v3f69-abierto`, y esa clase NO se pone
   cuando la sección abierta es #v3-pl-docs (ver v3f69Sync: en ese caso la
   columna queda en `.v3f69-libre`, que no tiene ni una regla). Resultado:
   Documentos se abre, crece a su tamaño natural y la columna se desplaza,
   exactamente como hoy.
   El motivo es clínico, no estético: cada formulario pesa más que la columna
   entera (receta 966 px, orden 1.003, control 1.056) y el acordeón interno no
   alivia nada. Metiéndola al reparto, la superficie para escribir una receta
   caía de 605 px a 24-169 según la pantalla. **La receta no se degrada en
   ninguna pantalla.**
   Lo que Documentos SÍ conserva: arranca cerrada y participa de la
   exclusividad. Lo único que no hereda es el reparto de alto. */

/* La sección abierta se come TODO el alto que sobra. Ahí muere el hueco.
   El piso de 120 px es una RED, no el tamaño de trabajo: en cuanto hay
   sobrante, `flex-grow` la lleva hasta el pie de la columna. Sirve para que en
   una pantalla muy baja lo que se abre no nazca aplastado (sin él se midieron
   12 px de alto). No mueve el video: vive DENTRO de la columna de alto fijo. */
body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto .v3-pl[open] {
  flex: 1 1 auto;
  min-height: 120px;
  display: flex;
  flex-direction: column;
}

/* La caja intermedia de Chromium: es ELLA la que hay que dejar crecer y
   encoger, no el cuerpo. Es el eslabón que faltaba. */
body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto .v3-pl[open]::details-content {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Mientras hay algo abierto (y no es Documentos), las secciones cerradas se
   compactan —se pliega su bajada, no su título— y el hueco baja a 10 px.
   Cerrado todo, la columna vuelve a respirar como en el mockup. */
body.portal-v3 .sesion-sala.modo-jitsi .sesion-panel.v3f69-abierto {
  gap: 10px;
}
body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto .v3-pl:not([open]) > summary {
  min-height: 48px;
  padding-top: 9px;
  padding-bottom: 9px;
}
body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto .v3-pl:not([open]) .v3-pl__sub {
  display: none;
}

/* …y el que se desliza es su propio cuerpo, no la columna entera. */
body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto .v3-pl[open] > .v3-pl__cuerpo {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* 🔴 DIRECTRIZ 1 DE MAURICIO — LA COLUMNA NUNCA RECORTA.
   Acá había un `overflow:hidden`. Lo mató el NO-GO de Renata: a 674 px de alto
   dejaba «Resumen y firma» 73 px fuera y «Documentos del paciente» 7 px,
   cortados e inalcanzables, sin un solo error. La columna conserva SIEMPRE el
   `overflow-y:auto` del §1.4: si no cabe, se desplaza, como hoy.
   Contenido que no se puede alcanzar es peor que cualquier hueco feo. */

/* Con TODO cerrado, el pie crece y llena el espacio con el resumen honesto
   (qué se envió, a qué hora, qué falta). Es justo lo que hoy nadie ve. */
body.portal-v3 .sesion-sala.modo-jitsi .sesion-panel.v3f69-cerrado #v3-sobre {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* …y con algo abierto el resumen se achica, que es el punto 4 del mockup
   («el resumen se achica a una línea»). No es cosmético: es el espacio que le
   cede a la sección abierta. Sin esto la columna queda en déficit y lo que se
   abre nace aplastado (medido: 12 px de alto).
   Se conservan título y bajada —la médica sigue sabiendo qué hay ahí— y se
   pliega SOLO el detalle. */
body.portal-v3 .sesion-sala.modo-jitsi .sesion-panel.v3f69-abierto #v3-sobre {
  flex: 0 1 auto;
  min-height: 0;
  /* `auto`, NUNCA `hidden`: aunque se compacte, lo que quede dentro tiene que
     poder alcanzarse (directriz 1). */
  overflow-y: auto;
}
body.portal-v3 .sesion-sala.modo-jitsi .sesion-panel.v3f69-abierto #v3-sobre-items,
body.portal-v3 .sesion-sala.modo-jitsi .sesion-panel.v3f69-abierto #v3-sobre .v3-sobre__sub {
  display: none;
}

/* La acción de la sección queda anclada a su pie, SIEMPRE en el mismo píxel.
   Se hace con `sticky`, NO moviendo el nodo: `.mrs-acc` lo recrea un
   innerHTML en cada repintado (v3MrsPanelPintar) y un nodo mudado bajo un
   innerHTML posterior se pierde — es una trampa ya pagada en este proyecto.
   Los dos son hijos DIRECTOS del scroller, que es lo que `sticky` necesita.
   El margen negativo cancela el `padding: 0 18px 18px` del cuerpo para que el
   pie llegue de borde a borde, como en el mockup. */
body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto .v3-pl[open] > .v3-pl__cuerpo > .mrs-acc,
body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto .v3-pl[open] > .v3-pl__cuerpo > .v3-doc__pie {
  position: sticky;
  bottom: 0;
  z-index: 1;
  margin: 12px -18px -18px;
  padding: 12px 18px;
  background: #fff;
  border-top: 1px solid #ece7f5;
}

/* Sin `min-height:0` una sección CERRADA podría negarse a encogerse y empujar
   a la abierta fuera de la caja. Barato y explícito. */
body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto .v3-pl:not([open]) {
  flex: 0 0 auto;
  min-height: 0;
}

/* 🔴 DIRECTRIZ 2 DE MAURICIO — la tarjeta de la paciente se compacta a UNA
   LÍNEA mientras hay algo abierto, y vuelve entera al cerrar todo.
   El criterio es «la identidad SIEMPRE visible», y una línea con el nombre y
   el sello «Verificada» lo cumple: lo que no puede pasar es que desaparezca.
   Se pliegan los antecedentes y el pie («Ver su historia completa»); se
   conserva `.pac-ctx__id`, que es nombre + datos + sello.
   Son los ~85 px que más pesan del presupuesto: sin esto no cabe. */
body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto #pac-ctx .pac-ctx__chips,
body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto #pac-ctx .pac-ctx__pie {
  display: none;
}

}  /* fin de @media (min-height: 950px) */
}  /* fin de @supports selector(::details-content) */


/* ══════════════════════════════════════════════════════════════════════════
   §F72b · EL HUECO DE LA COLUMNA DERECHA — LO QUE SÍ SE PUEDE CERRAR
   ══════════════════════════════════════════════════════════════════════════

   🔴 LEER ANTES DE TOCAR: lo que estas reglas NO hacen.
   El formulario de documentos NO entra al reparto de alto. Sigue valiendo el
   motivo clínico de la F69: receta 966 px, orden 1.003, control 1.056 — cada
   uno pesa más que la columna entera, y meterlos al reparto dejaba 24-169 px
   para escribir una receta. **La receta no se degrada en ninguna pantalla.**
   Acá solo se hace crecer al Sobre, que es texto de lectura, no superficie de
   escritura.

   ── 1 · El estado «Documentos abierto» (v3f69-libre) deja de ser un estado
   sin ninguna regla. Cuando sobra alto dentro de la columna, el que lo toma es
   el resumen del Sobre — que es justo la información que hoy nadie ve— y no el
   formulario. Si NO sobra alto (que es lo habitual en pantallas bajas), esta
   regla no hace nada: `flex-grow` solo reparte espacio LIBRE, nunca quita.
   Va fuera de @supports porque no depende de ::details-content: es flexbox
   corriente sobre el panel, que ya es una columna flex (§1.4). */
body.portal-v3 .sesion-sala.modo-jitsi .sesion-panel.v3f69-libre #v3-sobre {
  flex: 1 1 auto;
  min-height: 0;
  /* `auto` y NUNCA `hidden`: directriz 1 de Mauricio — la columna no recorta.
     Si el resumen crece más que su hueco, se desliza; no se corta. */
  overflow-y: auto;
}

/* ── 2 · El umbral baja de 950 a 640 px de alto de viewport, PERO SOLO para
   las reglas que no achican la superficie donde se escribe.

   🔴 QUÉ BAJA Y QUÉ NO, Y POR QUÉ
   BAJAN: compactar las cabeceras de las secciones CERRADAS, y dejar crecer al
   Sobre con todo cerrado. Las dos LIBERAN alto; ninguna toca lo que se está
   escribiendo.
   NO BAJAN (siguen gateadas a 950, tal como quedaron en la F69):
     · el `flex-grow` de la sección ABIERTA — es el que definía la superficie
       de escritura, y es exactamente lo que la aritmética del presupuesto
       autorizó recién a partir de 950;
     · el plegado del detalle del Sobre y el de la tarjeta de la paciente
       (`#pac-ctx`) con algo abierto — esos dos ESCONDEN información, y su
       precio solo se justifica cuando la sección abierta crece a cambio. Sin
       el crecimiento serían pérdida pura.

   🔴 POR QUÉ 640 Y NO 0
   Con la aritmética de la propia F69, el costo fijo de la columna sin nada
   abierto es 640,30 px (tarjeta 167,80 + 5 cabeceras 344,50 + 6 huecos 96 +
   Sobre 32). El panel vale 74vh. Bajo un viewport de ~865 px el panel ya mide
   menos que ese costo fijo: **la columna está en déficit y siempre se desliza**,
   así que compactar no puede abrir un hueco nuevo — solo reduce el
   desplazamiento. Por debajo de 640 px de viewport el panel choca con su
   `min-height: 440px` y compactar deja de mover la aguja; el piso está para no
   aplicar reglas donde ya no miden nada.
   Un notebook de 1536×864 da viewport ≈769 px: hoy no recibe NADA de la F69,
   y con este piso recibe lo que no le cuesta superficie.

   Se declara como bloque APARTE en vez de editar el @media existente: así el
   bloque de la F69 queda intacto, byte a byte, y quien lea el diff ve qué se
   agregó sin tener que reconstruir qué se movió. En la banda ≥950 las dos
   copias declaran lo mismo y no hay conflicto posible. */
@supports selector(::details-content) {
@media (min-height: 640px) {

  body.portal-v3 .sesion-sala.modo-jitsi .sesion-panel.v3f69-abierto {
    gap: 10px;
  }
  body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto .v3-pl:not([open]) > summary {
    min-height: 48px;
    padding-top: 9px;
    padding-bottom: 9px;
  }
  body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto .v3-pl:not([open]) .v3-pl__sub {
    display: none;
  }
  /* Sin `min-height:0` una sección cerrada podría negarse a encoger y empujar
     a la abierta fuera de la caja. Barato y explícito (igual que en la F69). */
  body.portal-v3 .sesion-sala.modo-jitsi .v3f69-abierto .v3-pl:not([open]) {
    flex: 0 0 auto;
    min-height: 0;
  }
  /* Con todo cerrado el resumen honesto llena el sobrante. */
  body.portal-v3 .sesion-sala.modo-jitsi .sesion-panel.v3f69-cerrado #v3-sobre {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
  }

}  /* fin de @media (min-height: 640px) */
}  /* fin de @supports selector(::details-content) */


/* ══════════════════════════════════════════════════════════════════════════
   §F72c · LA COLUMNA DERECHA LLEGA HASTA ABAJO  (decisión de Mauricio, 2-ago)
   ══════════════════════════════════════════════════════════════════════════

   EL PROBLEMA, MEDIDO SOBRE EL DOM REAL (no sobre una réplica):
   `.sesion-panel` vale `height: 74vh` y la malla trae `align-items: start`
   (estilos.css:1077), mientras la columna IZQUIERDA lleva el escenario (56vh)
   + la barra + las 5 tarjetas de #v3-acciones + la Nota clínica. La fila mide
   lo que mide la izquierda y bajo el panel queda blanco:

       1536×769 -> fila 1.186,1 px · panel 569 px · HUECO 617,1 px
       1440×769 -> fila 1.347,7 px · panel 569 px · HUECO 778,6 px
       1440×950 -> fila 1.449,1 px · panel 703 px · HUECO 746,1 px

   🔴 Esos números salieron recién cuando el banco incluyó `#v3-acciones` y
   `#v3-nota`, que los inyecta medico.js y NO están en el HTML estático. Con la
   izquierda incompleta el hueco medía 20,8 px y habría hecho «cerrar» un hueco
   que en producción seguía abierto. Es la lección de la F69, otra vez: el
   presupuesto de alto se mide sobre el DOM REAL.

   EL ARREGLO: el panel deja de tener un alto FIJO y pasa a ocupar su celda.

   🔴 POR QUÉ ESTO NO PUEDE QUITARLE SUPERFICIE A NADIE — que es la condición
   que puso Mauricio. El alto usado queda en
       clamp( min-height , alto de la celda , auto )
   y `min-height: max(440px, 74vh)` REPRODUCE EXACTAMENTE el piso de hoy
   (`height: 74vh` + `min-height: 440px` = max(74vh, 440px)). O sea: el panel
   puede crecer, nunca encoger. Crecer solo agrega superficie de escritura.
   Si alguna medición mostrara el panel MÁS CHICO que hoy, eso es un defecto y
   hay que parar: por eso el banco compara panelAlto vieja↔nueva en toda la
   matriz, no solo el hueco.

   🔴 POR QUÉ VA ACOTADO A ≥1181px
   Es la ÚNICA banda donde la malla tiene dos columnas y por lo tanto donde el
   hueco existe (medido: a 1.100 px de ancho la malla ya es de una columna y el
   hueco vale 0). Fuera de esa banda el panel es su propia fila y estirarlo
   cambiaría el desplazamiento en móvil sin arreglar nada. Mínima superficie.
   Con el cajón abierto el panel está en `display:none`, así que no le llega.

   🔴 LA COLUMNA SIGUE SIN RECORTAR: el `overflow-y: auto` del §1.4 no se toca.
   Si el contenido no cabe, se desliza. NUNCA `hidden` (directriz 1). Y ningún
   alto sale de JS: esto es reparto del navegador.

   🔴🔴 POR QUÉ ADEMÁS HAY TOPE DE VIEWPORT Y `sticky` — NO ES ADORNO
   Estirar A SECAS (solo `align-self: stretch`) cierra el hueco pero ROMPE un
   contrato aprobado de la F69: «la acción de la sección queda anclada a su pie,
   SIEMPRE en el mismo píxel». Ese pie es `position: sticky; bottom: 0` DENTRO
   del cuerpo de la sección, y sólo funciona si el panel cabe en la pantalla.
   Con el panel estirado a 1.502 px en un viewport de 950, el pie se ancla al
   fondo de una caja que está 550 px bajo el pliegue. MEDIDO:

       1440×950  · sección abierta · pie visible sin desplazar:
            hoy TRUE  ·  estirar a secas FALSE  ·  con tope+sticky TRUE
       1440×1080 · sección abierta:
            hoy TRUE  ·  estirar a secas FALSE  ·  con tope+sticky TRUE

   Un «Enviar» que hoy se alcanza y después no, es quitarle superficie útil a la
   médica aunque el número de píxeles suba. Con el tope, el panel crece igual
   (703→918 y 799→1.048; superficie de escritura 284→495 y 379→623) y el pie
   sigue donde estaba.
   Y el `sticky` es lo que hace que el hueco no se vea: el panel ACOMPAÑA a la
   médica mientras recorre la columna izquierda, así que a la derecha nunca hay
   blanco. Su bloque contenedor es la celda entera (por el `stretch`), de modo
   que puede viajar los ~450 px que mide el recorrido de la página.
   Verificado que ningún ancestro tiene `overflow` que rompa el `sticky`
   (`.sesion-sala`, `#pantalla-sesion`, `.contenedor` y `body` en `visible`).

   👉 Si Mauricio prefiere el estirado a secas, se borran las DOS declaraciones
   marcadas abajo y queda exactamente eso. */
@media (min-width: 1181px) {
  body.portal-v3 .sesion-sala.modo-jitsi .sesion-panel {
    align-self: stretch;
    height: auto;
    min-height: max(440px, 74vh);
    /* ↓ las dos que se borran para volver al estirado a secas ↓
       `min-height` SIEMPRE le gana a `max-height` en CSS, así que el piso de
       hoy queda garantizado incluso en ventanas muy bajas. */
    max-height: calc(100vh - 32px);
    position: sticky;
    top: 16px;
  }
}


/* ══════════════════════════════════════════════════════════════════════════
   §F73a · «DOCUMENTOS DE LA CONSULTA»: EL TÍTULO DEJA DE PARTIRSE LETRA A LETRA
   ══════════════════════════════════════════════════════════════════════════
   Medido: la fila es flex; el ícono (38 px) y la píldora de estado
   («sin constancia de envío», ~180 px) son `flex: none`, y el único que cede
   es el <span> del título. En la columna de la ficha quedan ~256 px de
   contenido, así que el título colapsaba a ~20 px y, con `overflow-wrap`,
   se partía carácter a carácter en vertical, con la píldora encima.

   Arreglo de menor superficie: la fila puede envolver, y el título tiene una
   base propia. Lo que baja de renglón es la PÍLDORA, no el nombre del
   documento. */
body.portal-v3 .v3d-ficdoc { flex-wrap: wrap; }

/* 🔴 El `:not()` no es adorno: en el marcado los TRES hijos son <span>
   (v3dChip devuelve un <span>, y la píldora también). Un `min-width: 140px`
   sin excluirlos le daría 140 px de ancho al ícono de 38 px y a la píldora.
   Se excluyen por clase, que es lo que los distingue. */
body.portal-v3 .v3d-ficdoc > span:not(.v3d-pill):not(.v3d-chipic) {
  flex: 1 1 160px;
  min-width: 140px;
}

/* La píldora ya no compite por el ancho del título, así que ahora sí conviene
   que su texto no se parta. **Acotado a esta fila**: `.v3d-pill` se usa en la
   agenda, en Mensajes y en el bloque MRS, y un `nowrap` global podría
   desbordar en cualquiera de esos. */
body.portal-v3 .v3d-ficdoc .v3d-pill { white-space: nowrap; }


/* ══════════════════════════════════════════════════════════════════════════
   §F73b · «SEGUIMIENTO»: LOS VALORES LARGOS DEJAN DE QUEDAR EN BANDERA
   ══════════════════════════════════════════════════════════════════════════
   La segunda celda de la mini-tabla está alineada a la derecha y en negrita,
   lo que sirve para un dato corto («Ninguno», una fecha). Pero una de las
   filas recibe una frase de ~107 caracteres, y a la derecha queda un bloque
   de texto con el borde izquierdo dentado: ilegible.

   Modificador `es-largo` (misma convención que `es-vacio`, `es-ok`,
   `es-borrador` que ya viven en esta hoja). El HTML lo pone medico.js — que
   NO toco: ver el informe, ahí va el cambio de marcado que hace falta.
   Se declaran las dos formas a propósito: la segunda gana por especificidad
   sobre la regla de la celda final, y la primera evita que el modificador
   quede MUDO si algún día se aplica a una celda que no es la última. Un
   modificador que no hace nada y no avisa es peor que no tenerlo. */
body.portal-v3 .v3d-minitabla td.es-largo,
body.portal-v3 .v3d-minitabla td:last-child.es-largo {
  text-align: left;
  font-weight: 500;
  /* Separa el valor de su rótulo ahora que los dos empiezan a la izquierda. */
  padding-left: 14px;
}

/* 🔴 EL PIE DE LA TARJETA NO SE DECLARA ACÁ.
   El botón suelto «Agendar control» necesita `.v3d-pie-card`, que YA EXISTE en
   esta hoja (borde superior + separación) y hoy no se usa. Redeclararla acá
   sería exactamente la trampa que ya se pagó en la 63c —dos reglas para lo
   mismo, una escondida mil líneas después— y la que vigila el gate permanente
   de la 62c. Si la clase existe, se usa; no se clona.
   Lo único que falta es envolver el botón en el marcado, y eso vive en
   medico.js: va descrito en el informe. */
