#view-teleprompter {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  background: #000;
  color: #fff;
}

.tp-progress {
  height: 6px;
  background: #1a1a1a;
  flex-shrink: 0;
}

.tp-progress__bar {
  height: 100%;
  width: 0%;
  background: var(--color-accent);
  transition: width 0.1s linear;
}

.tp-main {
  display: flex;
  flex: 1;
  min-height: 0;
}

/* Viewport + capa fija encima (zonas táctiles, avisos). El viewport scrollea;
   la capa no, por eso las zonas viven en ella y no dentro del viewport. */
.tp-stage {
  position: relative;
  display: flex;
  flex: 1;
  min-width: 0;
  min-height: 0;
  /* La botonera se va hacia abajo con translateY al colapsar; sin esto
     asomaría por debajo del stage (sobre la sidebar, en móvil). */
  overflow: hidden;
  /* El área de lectura es superficie de gesto: el scroll y el zoom nativos
     acá dentro pelearían con el arrastre de la cinta, y seleccionar texto
     mientras se habla no le sirve a nadie. */
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  /* Alto reservado para la botonera flotante. Lo consume .tp-hint para
     quedar SIEMPRE encima de ella, y se anima junto con el colapso. */
  --tp-controls-h: 5.5rem;
}

.tp-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.tp-viewport {
  position: relative;
  flex: 1;
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  padding: var(--space-5) var(--space-4);
  line-height: 1.4;
}

.tp-runup,
.tp-rundown {
  pointer-events: none;
}

.tp-text {
  max-width: 900px;
  margin: 0 auto;
}

/* Una línea DURA (las que el autor separó con Enter o con [[br]]) por bloque.
   El aire va como margin-top de la línea SIGUIENTE, nunca como una línea
   vacía: un div sin contenido no genera line box y tendría altura 0.

   El selector `+` es también la garantía de que la PRIMERA línea de un
   segmento nunca lleva margin-top. Si lo llevara, ese margen colapsaría hacia
   afuera de .tp-slide-segment (que no tiene padding ni borde) y corrompería el
   seg.top que mide measureWordPositions. splitHardLines refuerza lo mismo del
   lado del JS: lines[0].breakLevel siempre es 0.

   Sin `white-space`: los saltos son bloques reales, no whitespace preservado,
   así que no hacen falta las sorpresas de pre-wrap. */
.tp-line + .tp-line {
  margin-top: 0.3em;
}

/* Después del anterior a propósito: misma especificidad (0,2,0), gana el que
   viene último. Una línea en blanco en el texto = separación de párrafo. */
.tp-line + .tp-line--para {
  margin-top: 0.8em;
}

.tp-word {
  transition: background-color 0.1s ease, color 0.1s ease, box-shadow 0.1s ease;
  border-radius: 4px;
}

/* Formato inline (**negrita**, *itálica*, ==resaltado==, ~atenuado~). Cuatro
   canales visuales deliberadamente distintos, porque .tp-word.active ya se
   apropió de "fondo + color de texto" para marcar la palabra que se está
   leyendo y el formato tiene que seguir viéndose por debajo de ese pill.

   Ninguna de estas clases puede declarar `font-size`: el box-shadow del pill
   mide su spread en `em` del .tp-word, así que cambiar el em de una palabra
   cambiaría el grosor de su halo.

   El subrayado de ==resaltado== se corta entre palabra y palabra a propósito:
   el nodo de texto separador vive FUERA del .tp-word, así que "==dos
   palabras==" muestra dos subrayados y no uno continuo. Unirlos exigiría spans
   de separador, complicación sin retorno. */
.tp-b {
  font-weight: 700;
}

.tp-i {
  font-style: italic;
}

.tp-mark {
  border-bottom: 0.12em solid var(--color-fmt-mark);
}

.tp-dim {
  color: var(--color-fmt-dim);
}

/* Acento tónico: la sílaba que hay que golpear. overline y no otra cosa por
   cuatro razones que la descartan todo lo demás: no cambia la caja (cero
   impacto en measureWordPositions ni en las anclas de scroll), usa
   currentColor —así que sobre el pill turquesa de la palabra activa se adapta
   sola—, va ARRIBA y por lo tanto es ortogonal al border-bottom ámbar de
   ==resaltado== (una palabra puede ser las dos cosas), y cubre exactamente
   las letras marcadas. Y no declara font-size, que está prohibido acá. */
/* Acento tónico: la sílaba que hay que golpear.
   
   Empezó como `overline` y no servía: la línea cae en el hueco ENTRE
   renglones, así que a distancia de teleprompter no se sabe si marca la
   sílaba de abajo o subraya la línea de arriba. Ahora la señal va sobre las
   propias letras —color— más un subrayado pegado a ellas, que no deja dudas
   de a qué pertenece.
   
   El color es AMARILLENTO Y DE BAJO CONTRASTE contra el texto: un color
   fuerte competía con la palabra en vez de marcarla dentro de ella. El
   subrayado ya hace el trabajo de señalar.
   
   Dos canales y no uno porque se degradan distinto: sobre el pill de la
   palabra activa el color se pierde (ahí manda la tipografía del pill, por la
   regla de abajo), pero el subrayado sobrevive tomando ese mismo color. Así
   la marca sigue estando justo cuando se la está leyendo.
   
   No colisiona con ==resaltado==, que usa `border-bottom` ámbar: es otra
   propiedad, otro color y otra altura, así que una palabra puede ser las dos
   cosas y se distinguen. Y no declara `font-size`, que está prohibido acá
   porque el pill mide su box-shadow en em. */
.tp-s {
  color: var(--color-fmt-stress);
  text-decoration: underline;
  text-decoration-thickness: 0.1em;
  text-underline-offset: 0.15em;
  text-decoration-color: var(--color-fmt-stress);
}

.tp-word.active .tp-s,
.tp-word.active.tp-s {
  text-decoration-color: currentColor;
}

/* Los sub-spans solo existen cuando el formato cae a mitad de una palabra. */
.tp-fmt {
  transition: color 0.1s ease;
}

.tp-word.active {
  background: var(--color-accent);
  color: #06231f;
  /* box-shadow instead of padding: a visual "pill" without changing the
     word's box size, which would reflow neighboring words / push them to
     a different line every time the highlight moves. */
  box-shadow: 0 0 0 0.08em var(--color-accent);
}

/* Un sub-span atenuado tiene `color` DECLARADO, y un color declarado le gana a
   la herencia sin importar la especificidad del padre — quedaría gris #9aa0a8
   sobre el pill turquesa, ratio ~1.6:1, ilegible. Este selector (0,3,0) es lo
   que lo devuelve al color del pill mientras la palabra está activa.
   (Para las clases puestas en el propio .tp-word no hace falta nada: .tp-word.active
   es (0,2,0) y .tp-dim (0,1,0), así que el pill ya gana.) */
.tp-word.active .tp-fmt {
  color: inherit;
}

/* Subtle line between slides: the talk reads as one continuous text, the
   separator only hints at the slide boundary. Kept small on purpose — a big
   gap here is scroll distance that has to be crossed at the slide change. */
.tp-separator {
  height: 1px;
  margin: 0.6em auto;
  max-width: 200px;
  background: var(--color-border);
}

.tp-sidebar {
  flex-shrink: 0;
  width: clamp(120px, 18vw, 220px);
  border-left: 1px solid var(--color-border);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  padding: var(--space-3);
  padding-right: calc(var(--space-3) + env(safe-area-inset-right, 0px));
  font-variant-numeric: tabular-nums;
}

/* Es un <button> porque abre la grilla de slides, pero tiene que seguir
   leyéndose como el número grande que era. */
.tp-sidebar__slide {
  font-size: clamp(1.75rem, 4.5vw, 2.75rem);
  font-weight: 700;
  color: #fff;
  background: none;
  border: 0;
  padding: 0;
  font-variant-numeric: tabular-nums;
}

/* El número más grande de la pantalla, a propósito: es el que se mira de
   reojo sin dejar de hablar. */
.tp-sidebar__remaining {
  font-size: clamp(2.5rem, 9vw, 4.5rem);
  font-weight: 700;
  line-height: 1;
  color: var(--color-accent);
  font-variant-numeric: tabular-nums;
}

.tp-sidebar__remaining.is-low {
  color: var(--color-danger);
}

.tp-sidebar__timer {
  font-size: clamp(1.3rem, 3.5vw, 2rem);
  color: var(--color-accent);
  text-align: center;
  line-height: 1.3;
}

.tp-sidebar__timer-sep {
  display: block;
  color: var(--color-text-muted);
  font-size: 0.7em;
}

@media (max-width: 520px) {
  .tp-main {
    flex-direction: column;
  }

  .tp-sidebar {
    width: auto;
    flex-direction: row;
    border-left: none;
    border-top: 1px solid var(--color-border);
    gap: var(--space-4);
    padding: var(--space-2);
    /* Acá la sidebar es la fila de MÁS ABAJO de la pantalla (la botonera
       flota dentro del stage, que queda encima), así que el inset del home
       indicator le toca a ella. */
    padding-bottom: calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
  }

  /* ...y por lo tanto la botonera deja de necesitarlo. */
  .tp-controls {
    padding-bottom: var(--space-3);
  }

  .tp-sidebar__timer-sep {
    display: inline;
  }
}

/* FUERA DEL FLUJO, a propósito. Mientras estuvo en la columna, colapsarla
   agrandaba el viewport; measureWordPositions redimensiona el espaciador
   inicial a clientHeight * READING_LINE_FRACTION, así que el relayout corría
   TODO el texto medio alto de botonera hacia abajo, de golpe y 150ms después
   de terminar la transición (el debounce es más corto que la animación, así
   que no había relayout intermedio que lo repartiera). Flotando, ocultarla no
   cambia una sola medida: cero relayout, cero salto.

   Por eso también la transición es de transform y no de max-height: un
   max-height animado vuelve a mover el layout frame a frame. */
.tp-controls {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 4;
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3) calc(var(--space-4) + env(safe-area-inset-right, 0px)) calc(var(--space-4) + env(safe-area-inset-bottom, 0px)) calc(var(--space-4) + env(safe-area-inset-left, 0px));
  opacity: 1;
  transform: translateY(0);
  /* Flota sobre el texto, así que necesita fondo propio: opaco de verdad a lo
     largo de toda la banda de botones —si no, las palabras se leen ENTRE los
     botones— y recién arriba de ellos se desvanece, para que el texto que
     sigue no quede cortado por un borde duro. */
  background: linear-gradient(to top, rgba(0, 0, 0, 0.97) 0, rgba(0, 0, 0, 0.97) 78%, rgba(0, 0, 0, 0) 100%);
  padding-top: var(--space-5);
  transition: opacity 0.4s ease, transform 0.4s ease;
}

#view-teleprompter.kiosk-hidden .tp-controls {
  opacity: 0;
  pointer-events: none;
  transform: translateY(100%);
}

#view-teleprompter.kiosk-hidden .tp-stage {
  --tp-controls-h: 0rem;
}

/* Mini-botonera fija: anterior / play-pausa / siguiente. A diferencia de
   .tp-controls, NUNCA se oculta — el modo kiosco no la toca. Es el camino
   rápido para corregir el rumbo sin esperar a que aparezca la botonera
   grande, que es lo que costó tiempo en el ensayo. */
.tp-mini {
  position: absolute;
  top: calc(env(safe-area-inset-top, 0px) + var(--space-3));
  right: calc(env(safe-area-inset-right, 0px) + var(--space-3));
  z-index: 10;
  display: flex;
  gap: var(--space-2);
}

.tp-mini button {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
  background: var(--color-bg-elevated);
  color: #fff;
  border: 1px solid var(--color-border);
  opacity: 0.85;
}

.tp-mini #tp-mini-playpause {
  font-size: 1.2rem;
  border-color: var(--color-accent);
}

.tp-controls button {
  flex: 1;
  padding: var(--space-3);
  font-size: 1.3rem;
  background: var(--color-bg-elevated);
  color: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}

.tp-controls #tp-playpause {
  background: var(--color-accent);
  color: #06231f;
  flex: 2;
}

.tp-empty {
  padding: var(--space-5) var(--space-4);
  text-align: center;
}

.tp-empty a {
  color: var(--color-accent);
}

.tp-end {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-2);
  padding: var(--space-4);
}

.tp-end__title {
  font-size: 1.8rem;
  margin: 0;
}

.tp-end__time {
  font-size: 4rem;
  font-weight: 700;
  color: var(--color-accent);
  margin: 0;
  font-variant-numeric: tabular-nums;
}

.tp-end__actions {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.tp-end__actions button {
  padding: var(--space-3) var(--space-4);
  font-size: 1.1rem;
  background: var(--color-bg-elevated);
  color: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}

.tp-end__actions #tp-end-restart {
  background: var(--color-accent);
  color: #06231f;
  border: none;
}

/* Línea de lectura, visible solo mientras se arrastra la cinta: sin ella, el
   dedo no tiene contra qué alinear.

   El + var(--space-5) no es decorativo. El motor deja la palabra activa a
   clientHeight * READING_LINE_FRACTION del inicio del CONTENIDO del viewport,
   y el viewport tiene padding-top var(--space-5), así que la línea real está
   ese padding más abajo del 50% de la caja. (El overlay mide lo mismo que el
   viewport: los dos llenan .tp-stage.) */
.tp-readline {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(50% + var(--space-5));
  height: 2px;
  background: var(--color-edition);
  opacity: 0;
  transition: opacity 0.15s ease;
}

#view-teleprompter.scrubbing .tp-readline {
  opacity: 0.85;
}

/* La palabra donde caería el seek si se soltara ahora. Va por outline —que no
   ocupa espacio y por lo tanto no reflowea el texto bajo el dedo— y en el
   lavanda de la edición: el turquesa ya se lo apropió .active, y confundirlos
   sería confundir "dónde estoy" con "dónde voy". Sin font-size: el pill de la
   palabra activa mide su halo en em. */
.tp-word.candidate {
  outline: 2px solid var(--color-edition);
  outline-offset: 2px;
}

/* Avisos (confirmación de fin, fin de slide en modo libre). Flotan sobre el
   texto, centrados abajo del área de lectura, y no capturan toques. */
.tp-hint {
  position: absolute;
  left: 50%;
  bottom: calc(var(--space-3) + var(--tp-controls-h));
  transition: bottom 0.4s ease;
  transform: translateX(-50%);
  padding: var(--space-2) var(--space-3);
  border-radius: 999px;
  background: rgba(20, 23, 28, 0.92);
  border: 1px solid var(--color-border);
  color: var(--color-text);
  font-size: 0.95rem;
  white-space: nowrap;
  pointer-events: none;
  z-index: 3;
}

/* Pantalla de fin: tiempo por slide. */
.tp-end__slides {
  list-style: none;
  margin: var(--space-3) auto;
  padding: 0;
  width: min(100%, 320px);
  max-height: 40vh;
  overflow-y: auto;
  display: grid;
  gap: 4px;
  font-variant-numeric: tabular-nums;
}

.tp-end__slide {
  display: grid;
  grid-template-columns: 2.5em 1fr auto;
  gap: var(--space-2);
  padding: 2px var(--space-2);
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.04);
}

.tp-end__n {
  color: var(--color-text-muted);
  text-align: right;
}

.tp-end__delta {
  color: var(--color-text-muted);
}

.tp-end__delta.is-over {
  color: var(--color-danger);
}

.tp-end__delta.is-under {
  color: var(--color-accent);
}

/* Grilla de slides. Para un desfase de varias slides, arrastrar es mucho
   recorrido y ⏭ son varios toques; un número es uno solo. */
.tp-jump {
  position: absolute;
  inset: 0;
  z-index: 15;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Opaco casi del todo: con el texto de la charla compitiendo por detrás,
     los números dejan de leerse como una elección. */
  background: rgba(0, 0, 0, 0.96);
  padding: var(--space-4);
}

/* Una tarjeta, no elementos sueltos flotando sobre el texto: hay que
   reconocer de un vistazo que esto es un menú y no parte de la charla. */
.tp-jump__panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  width: min(100%, 420px);
  max-height: 100%;
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg-elevated);
}

.tp-jump__title {
  margin: 0;
  font-size: 1.05rem;
  text-align: center;
  color: var(--color-text-muted);
}

.tp-jump__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
  gap: var(--space-2);
  overflow-y: auto;
}

.tp-jump__grid button {
  background: #000;
}

.tp-jump__grid button {
  aspect-ratio: 1;
  font-size: 1.2rem;
  font-variant-numeric: tabular-nums;
  background: var(--color-bg-elevated);
  color: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}

/* Overlay de arranque. Cubre la vista entera —sidebar incluida— porque el modo
   de falla que ataca es olvidarse de darle play con los nervios del primer
   segundo: tiene que ser imposible de ignorar y tiene que irse con cualquier
   cosa que el dedo o el teclado hagan. Se quita del DOM al arrancar (no se
   esconde), así que su presencia ES el estado. */
.tp-start {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  background: rgba(0, 0, 0, 0.92);
  text-align: center;
  padding: var(--space-4);
}

.tp-start__play {
  font-size: clamp(3.5rem, 14vw, 6rem);
  line-height: 1;
  color: var(--color-accent);
}

.tp-start__label {
  font-size: clamp(1.5rem, 6vw, 2.25rem);
  font-weight: 700;
}

.tp-start__hint {
  color: var(--color-text-muted);
  font-size: 0.95rem;
}

/* Nota al pie de los tiempos por slide. Angosta a propósito: es contexto para
   leer los desvíos, no un párrafo para leer de corrido. */
.tp-end__note {
  max-width: min(100%, 340px);
  margin: 0 auto;
  font-size: 0.85rem;
  line-height: 1.4;
}

/* La etiqueta del número grande. Sin ella, "7" en el medio de la pantalla no
   dice de qué son esos 7. aria-hidden porque el número ya tiene su aria-label:
   un lector de pantalla leería lo mismo dos veces. */
.tp-sidebar__label {
  display: none;
  font-size: 0.75rem;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-align: center;
  line-height: 1.2;
}

/* El total, más chico y atenuado: el número que se mira de reojo sin dejar de
   hablar es el de la slide actual, no cuántas hay. */
.tp-sidebar__of {
  font-size: 0.55em;
  color: var(--color-text-muted);
}

/* ---- Escritorio ----------------------------------------------------------
   Mismo layout que ya rige por encima de 520px —.tp-main es flex y la sidebar
   ya es una columna a la derecha—: acá solo se le dan las medidas del diseño.
   1024px es el mismo breakpoint que declara js/responsive.js y que usa la
   configuración.

   La BOTONERA NO se mueve al riel, aunque el prototipo la dibuje ahí. Flota
   fuera del flujo a propósito (ver el comentario largo de .tp-controls) y
   .tp-stage tiene overflow:hidden justamente para contenerla, así que no hay
   CSS que la saque; habría que re-parentarla con JS. Y el handoff dice que el
   teleprompter no cambia de comportamiento. */
@media (min-width: 1024px) {
  .tp-viewport { padding: 56px 64px; }

  .tp-sidebar {
    width: 232px;
    gap: var(--space-3);
    border-left: 1px solid var(--color-border);
  }

  .tp-sidebar__slide { font-size: 2.5rem; }
  .tp-sidebar__remaining { font-size: 4.75rem; }
  .tp-sidebar__timer { font-size: 1.25rem; }
  .tp-sidebar__label { display: block; }
}
