/* ═══════════════════════════════════════════════════════════
   Planifácil · Design System
   ═══════════════════════════════════════════════════════════ */

/* ── Tipografía de marca ──
   Fraunces variable (roman, subset latin) auto-alojada: la CSP solo permite
   fuentes 'self'. Fallback Georgia (formas afines) + font-display swap. */
@font-face {
  font-family: 'Fraunces';
  src: url('assets/fonts/fraunces-latin-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ── Tokens ──
   Valores del tema claro. El oscuro NO es otra hoja: redefine estos mismos tokens en
   :root[data-theme="dark"] (más abajo), y theme.js fija ese atributo antes del primer
   pintado leyendo localStorage o prefers-color-scheme. `color-scheme` acompaña al tema
   para que el navegador pinte selects, inputs y scrollbars en consecuencia.
   Regla de color de todo el sitio: el verde —sólido o --color-primary-soft— queda reservado para
   "esto es una capacidad real, con su precio". Lo decorativo va en neutro. Cuando el mismo
   verde marca a la vez un precio, una etiqueta y un adorno, deja de significar nada. */
:root {
  color-scheme: light;

  /* Salvia. Sustituye al turquesa #0F766E con el mismo papel: identidad, acción primaria y
     "estás aquí". Blanco encima da 5.95:1.
     El verde se queda porque el turquesa ya era verde-azulado: el salto de identidad es el
     más corto posible hacia una paleta cálida, y la regla de siempre —el color primario
     queda reservado para "esto es una capacidad real, con su precio"— sigue diciendo lo
     mismo sin reescribirse.
     El -ink NO es decoración: el valor base es para RELLENO, con --color-on-accent encima.
     Como texto sobre un tinte claro, #4F6B47 da 4.98 sobre --color-primary-soft, al filo;
     el -ink lo sube a 7.19. La regla es: base para rellenar, ink para escribir. */
  --color-primary:        #4F6B47;
  --color-primary-hover:  #3F5738;
  --color-primary-active: #33472E;
  --color-primary-soft:   #E6EDE2;
  --color-primary-mid:    #BFD3B8;
  --color-primary-ink:    #3B5235;

  /* El acento cálido, que ahora es durazno y no amarillo. La regla dura no cambia: JAMÁS
     como texto. #E8A87C sobre la tarjeta da 2.01, que no llega ni al 3:1 rebajado del texto
     grande; es un color de RELLENO, con --color-on-warm encima, o de trazo decorativo.
     Para escribir está el -ink, 7.24 sobre la tarjeta.
     Es igual en los dos temas: un durazno claro funciona como relleno sobre fondo claro y
     sobre fondo oscuro, y lo que cambia es la tinta, no él. */
  --color-accent:        #E8A87C;
  --color-accent-soft:   #FBEBDF;
  /* Arcilla: tercer acento, aún más escaso (un borde, nunca áreas). Baja de #E76F51 a este
     tono más profundo por una razón que aparece sólo con el acento en durazno: dos naranjas
     claros en la misma pantalla no son dos acentos, son uno mal impreso. El salto de
     luminancia es lo que los separa.
     Aquí queda SÓLO lo decorativo: filos de tarjeta, un número que salta al pasar el puntero,
     el icono de una ficha de confianza. Todo lo que significa PELIGRO vive en la familia
     --color-danger: tener "peligro arcilla" y "peligro rojo" en la misma pantalla es tener
     dos peligros. */
  --color-coral:      #B5654F;
  --color-coral-ink:  #8E4530;

  /* La familia de texto pasa de gris azulado a tinta parda. NO es un detalle: el gris frío
     sobre papel crema es lo que hacía que la calidez se leyera como un filtro amarillo
     encima de una interfaz fría, y no como papel. Sobre la tarjeta --color-text da 12.41 y
     --color-text-secondary 7.36; el peor caso de los cinco tonos es --color-text-tertiary
     sobre un input, 4.96. Todos en AA. */
  --color-heading: #232519;
  --color-text:    #333524;
  --color-text-secondary: #545745;
  /* El terciario se usa en texto de 10 a 12px (pie de planes, estado de autoguardado,
     encabezados del selector de OA, pies de documento), así que debe cumplir 4.5:1 igual que
     el resto pese a ser el más claro de los tres. Da 4.73 sobre la página, que es su peor
     fondo. Queda cerca del secundario: eso es lo que cuesta AA a 12px, y si algún día hace
     falta un tercer nivel hay que buscarlo por tamaño o peso, no por más claridad. */
  --color-text-tertiary:  #63664F;

  /* ── Fondos: TRES planos, no dos ──
     Antes eran cremas cálidos (#f7f3e9 / #fffdf7 / #f2ede1 / #e5dcc9). Se aclararon para
     quitar el velo amarillo que la calidez ponía sobre toda la interfaz, y ahí se cometió un
     error medible: `--color-background` y `--color-surface` quedaron a 1.065 de separación, y como .card y .panel
     usaban AMBOS `--color-surface`, una tarjeta sobre un panel quedaba a 1.000 — el mismo color
     exacto. Con el borde a 1.34 y las sombras casi invisibles sobre blanco, las tarjetas
     dejaron de tener contorno: flotaban en un campo blanco indiferenciado.
     Ahora hay tres planos con nombre y función, y cada uno se separa del que tiene debajo:
       --color-background      la página, el plano hundido
       --color-surface el panel, el contenedor
       --color-card   la tarjeta y la hoja del documento: el plano elevado, lo que se manipula
     Y `--color-border` sube a 1.50 sobre el papel, que es donde un borde de 1px empieza a leerse de
     lejos. Verificado que las cinco combinaciones de texto siguen en AA: el peor caso es
     --color-text-tertiary sobre la página, 4.56. */
  /* EL PAPEL VUELVE A SER CÁLIDO, y esta vez sin el velo. Los tres planos pasan de verdes
     fríos a lino, pero la lección del párrafo de arriba manda: la calidez NO se consigue
     subiendo la saturación del fondo —eso es lo que puso el filtro amarillo sobre todo— sino
     bajando el croma y llevando la tinta al pardo. #E8E6DA tira a lino, no a mantequilla, y
     el trabajo de "esto es cálido" lo hacen el durazno del acento y la familia de texto.
     Los tres escalones, medidos: panel sobre página 1.14 · tarjeta sobre panel 1.09 · borde
     sobre tarjeta 1.62. Los tres por encima de los de la paleta fría que sustituyen, así que
     el fallo de las tarjetas sin contorno no puede volver por aquí. */
  --color-border:         #CDCBB8;
  --color-background:     #E8E6DA;
  --color-surface:        #F6F4EC;
  /* Los inputs van HUNDIDOS respecto del panel que los contiene: si se acercan demasiado a
     él, un campo vacío deja de parecer un campo. Y eso es exactamente lo que pasó al pasar a
     lino: el primer valor daba 1.086 contra el panel —por debajo del 1.128 de la paleta fría
     que sustituía— y los campos de "Ajustes de la experiencia" se leían como fondo, no como
     algo que se puede tocar. Este da 1.274 contra la tarjeta y 1.168 contra el panel. */
  --color-surface-sunken: #E7E3D3;
  /* El borde de un control interactivo. WCAG 1.4.11 pide 3:1 y el valor anterior no llegaba,
     así que el control se distinguía por fondo hundido MÁS borde. Este SÍ llega: 3.23 sobre
     la tarjeta. Es la primera vez en la hoja que un control se sostiene por su contorno solo,
     y por eso se aprovecha el cambio de paleta para subirlo. */
  --color-border-strong:  #948F74;

  /* Salvia profunda, DELIBERADAMENTE igual en los dos temas: es el fondo de las bandas
     invertidas de la landing, que son piezas de color fijo —como los avisos— y no siguen al
     tema. En claro coincide con --color-primary-ink; en oscuro no puede seguirlo, porque
     ahí el primario ACLARA y la banda se volvería un bloque brillante con texto blanco
     encima. Sirve además de tinta para el botón blanco que va dentro: 7.85 sobre blanco. */
  --color-primary-deep:   #3B5235;

  /* Los canales sueltos del primario, para los halos y sombras que necesitan alfa. Existe
     porque un rgba() con el color escrito a mano no lo ve ningún buscar-y-reemplazar de
     hexadecimales: cuatro se habían quedado en el verde viejo hasta aquí. Se declara en los
     dos temas para que el halo de foco y la sombra del CTA sigan al tema como el resto. */
  --color-primary-rgb:    79 107 71;

  /* El violeta de la IA. No le quita significado a nada: ocupa un hueco vacío. Hasta aquí
     el mismo color primario marcaba a la vez la acción, la capacidad de pago, el estado
     correcto y la generación con IA, y por eso un botón que gasta dos créditos era
     indistinguible de uno que sólo guarda.
     REGLA: violeta = gasta un crédito de IA. Salvia = todo lo demás. Sin excepciones, o
     deja de poder explicarse.
     Se calienta un punto para convivir con el papel de lino, sin moverse de familia: el
     violeta tiene que seguir siendo violeta o la regla deja de leerse.
     Base para relleno (--color-on-accent encima da 5.42), -ink para escribir (6.21 sobre el
     -soft, 7.14 sobre la tarjeta). */
  --color-ai:        #7B57B5;
  --color-ai-hover:  #66469B;
  --color-ai-active: #553A82;
  /* La pista de la barra de créditos. Es un gráfico que informa, así que 1.4.11 le pide 3:1
     contra el relleno: el primer valor que se probó daba 1.75 y habría empeorado lo que
     había. Este da 3.58. */
  --color-ai-mid:    #D8CDEB;
  --color-ai-soft:   #F1ECF9;
  --color-ai-ink:    #66469B;

  /* La chispa. Salvia y violeta tienen luminancia 0.126 y 0.144: en escala de grises son
     CASI EL MISMO GRIS, así que para quien no distingue el tono el color aquí informa poco.
     Por eso ninguna pieza en violeta se queda sola con el color; todas llevan la chispa, o
     la palabra "Generar", o las dos. Va por máscara con background: currentColor, el mismo
     patrón que los avisos, así que hereda el color sin una regla por tema. */
  --icon-spark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M16 5 C16.6 11 21 15.4 27 16 C21 16.6 16.6 21 16 27 C15.4 21 11 16.6 5 16 C11 15.4 15.4 11 16 5 Z' fill='black'/%3E%3C/svg%3E");
  /* El galón de "esto se abre". Estaba escrito a mano en cinco sitios —las preguntas
     frecuentes, el ejemplo del documento, los ajustes— y ahora lo necesita también la flecha
     del desplegable, que en modo estilable es un triángulo relleno del navegador y no el
     galón que usa el resto del producto. Cinco copias de un mismo trazo son cinco sitios
     donde cambiarlo. Mismo patrón que la chispa: máscara sobre un fondo de color. */
  --icon-caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  /* --color-card NO es --color-surface: es el plano ELEVADO. Lo usan la hoja del documento (barra de
     formato, título editable, celdas de la ficha) y las tarjetas, que conceptualmente son lo
     mismo — la pieza que se manipula, levantada sobre el contenedor que la agrupa. Que las
     tarjetas usaran --color-surface, el mismo token que su panel, es lo que las dejó sin contorno. */
  --color-card:   #FFFEF9;
  /* Lo que va ENCIMA de un relleno de acento. Un token en vez de un #fff clavado en diez
     selectores: en oscuro los rellenos se aclaran y el texto tiene que invertirse, y eso
     eran once reglas de ajuste que ahora no hacen falta. */
  --color-on-accent: #fff;

  /* ── Estados ──
     El comentario anterior decía que dos parejas "se funden aquí". No se fundieron: los
     valores siguieron escritos a mano en .btn.danger, .notice-*, .status-* y .toast--*,
     y nueve de estos tokens llevaban declarados sin que NADIE los usara. Ahora sí pasan
     todos por aquí, que es lo que convierte el cambio de paleta en editar este bloque en
     vez de rastrear noventa y cuatro líneas.
     Al cablearlos aparecieron cuatro rojos y cuatro ámbares donde debería haber uno de
     cada. Los dos que diferían en 2/255 —imperceptibles, salidos de copiar y pegar— sí se
     funden ahora. Los otros tres ámbares difieren de verdad (Δ25 a Δ102) y hacen trabajos
     distintos, así que se nombran en vez de aplastarse:
       --color-warning        el trazo y el borde
       --color-warning-strong la tinta de la píldora de estado, sobre --color-warning-soft
       --color-warning-deep   el relleno del aviso flotante, con texto blanco encima
       --color-warning-ink    la tinta del aviso en bloque, la más oscura de las tres */
  /* Un rojo algo más profundo que el anterior, porque ahora carga con lo que traía el
     terracota: con blanco encima da 6.57, que es justo lo que le faltaba a la cuenta de
     avisos sin leer (3.09 con el coral). */
  --color-danger:       #B42318;
  --color-danger-hover: #8F1D13;
  /* Los tres tintes de estado se recalientan sobre el papel de lino. No es cosmética: un
     rosa con base azul (#fff2f0) sobre crema se lee gris sucio, no rosa, y el aviso pierde
     el color que lo hace un aviso. */
  --color-danger-border:  #E3B7B0;
  --color-danger-soft:    #FDF1EE;
  --color-danger-ink:   #5C1A16;
  /* El ámbar baja de #c97d00. Con el acento en durazno, un ámbar claro y el acento quedaban
     a un paso; el ámbar se oscurece para que "advertencia" y "decoración cálida" no compartan
     tono. */
  --color-warning:         #B47500;
  /* Sobre --color-warning-soft es la tinta de la píldora "te quedan pocos créditos": aquí da
     5.44, y 5.85 sobre la tarjeta, que es su otro fondo. */
  --color-warning-strong:  #8F5C00;
  --color-warning-deep:    #7A4E00;
  --color-warning-soft:      #FBF2DC;
  --color-warning-ink:     #452D00;
  /* El verde de "correcto" tiene ahora la misma familia que la marca, así que se separa por
     LUMINANCIA y no por tono: el tinte es más claro y la tinta más oscura que el par
     primario. Donde se usa —.notice-success— lleva además su filo lateral. */
  --color-success-soft:        #E6F1E4;
  --color-success-ink:       #2B4A26;
  --color-accent-ink:    #8A4218;

  /* Lo que se escribe ENCIMA del relleno de acento. Es fijo en los dos temas porque el
     durazno también lo es: --color-on-accent no sirve aquí, porque en claro vale blanco y
     el blanco sobre durazno da 2.01. */
  --color-on-warm:       #1F0F05;

  /* El tinte de la sombra sale de --color-heading, ahora una tinta parda (#232519), y vuelve
     a ser cálido. El comentario anterior decía que una sombra marrón "pedía el crema que ya
     no está": el crema está de vuelta, así que la sombra vuelve con él. Una sombra gris
     azulada sobre papel de lino se ve como suciedad, no como profundidad.
     Las opacidades se mantienen: lo que hace visible a una sombra sobre crema es el
     contraste de color, no el de luminancia. */
  --shadow-xs: 0 1px 2px rgba(35,37,25,.07);
  --shadow-sm: 0 2px 8px rgba(35,37,25,.09), 0 1px 2px rgba(0,0,0,.04);
  --shadow:    0 4px 14px rgba(35,37,25,.10), 0 1px 3px rgba(35,37,25,.07);
  --shadow-lg: 0 14px 40px rgba(35,37,25,.16), 0 2px 8px rgba(35,37,25,.08);

  /* El gris de los esqueletos de carga. Va entre el borde y la superficie hundida: tiene que
     leerse tanto sobre la tarjeta blanca como sobre el carril hundido, que son los dos sitios
     donde se usa, sin llegar a parecer contenido. El brillo que lo recorre es un blanco con
     alfa, no un color: así funciona sobre cualquiera de los dos fondos. */
  --sk-bg:    #D8E4E0;
  --sk-glint: rgba(255,255,255,.62);

  /* La forma. Medido en planificada.cl, el radio que domina su interfaz es de 16px y el
     nuestro era de 6px: esa sola diferencia explica buena parte de por que su producto se
     lee como amable y el nuestro como administrativo. La escala sube entera, conservando
     los tres papeles: campos y fichas pequenas, tarjetas y botones, paneles y dialogos. */
  --r-sm: 10px;
  --r:    14px;
  --r-lg: 20px;
  /* La pildora, para la accion principal de cada pantalla. Es lo que ellos usan en
     "Generar Planificacion con IA" y en el conmutador de plan mensual/anual. */
  --r-pill: 999px;

  --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, Roboto, sans-serif;
  --serif: 'Fraunces', Georgia, 'Times New Roman', serif;
  --ease: 150ms ease;

  /* ── Escala tipográfica ──
     Sustituye a los ~25 tamaños sueltos que había (9, 9.5, 10, 10.5, 11, 11.5, 12, 12.5,
     13, 13.5, 14, 14.5, 15…) y que salieron de afinar cada componente por separado.
     La razón NO es constante, y eso es deliberado: entre 10 y 17px la diferencia que el
     ojo distingue es de ~1px, así que los saltos van cerca de 1.10; de 20px hacia arriba
     hacen falta saltos francos (1.20) para que la jerarquía se lea sin comparar dos textos
     lado a lado. Una sola razón geométrica falla en uno de los dos extremos. */
  /* Toda la escala sube un escalon. El cuerpo pasa de 15 a 16px, que es lo que usa
     planificada.cl y lo que recomienda cualquier guia para texto que se lee en tablet — que
     es donde se planifica. El extremo bajo (cejillas y badges, que van en versalitas con
     letter-spacing) se mueve menos: ahi crecer anade ruido, no legibilidad. */
  --fs-3xs: 10.5px;  /* cejillas, badges: siempre con letter-spacing */
  --fs-2xs: 12px;    /* micro-notas, pies de documento */
  --fs-xs:  13px;    /* pies, trustbar, footer */
  --fs-sm:  14px;    /* cuerpo de tarjeta, párrafo secundario */
  --fs-md:  16px;    /* cuerpo base = font-size del body */
  --fs-lg:  18px;    /* .lead, .card h3 */
  --fs-xl:  21px;    /* .card h2 */
  --fs-2xl: 26px;    /* .panel h2 */
  --fs-3xl: 34px;    /* .page-title de páginas internas */
  /* Fluidos: sólo para los titulares del sitio público. */
  --fs-display-lg: clamp(36px, 5vw, 52px);

  /* ── Pesos ──
     De nueve a seis. Los pesos "raros" (480, 530, 560) no eran un error: Fraunces es
     variable y a tamaño grande hay que compensar ópticamente hacia abajo. El error era
     tener cinco valores para tres papeles. La regla que los gobierna, en la serif:
     A MÁS TAMAÑO, MENOS PESO. */
  --w-display: 480;  /* serif ≥ 32px */
  --w-title:   530;  /* serif 20–31px */
  --w-sub:     560;  /* serif 15–19px */
  --w-ui:      500;  /* sans secundario: nav, opciones */
  --w-strong:  600;  /* sans con énfasis: h3, summary, botones */
  --w-label:   700;  /* sans en versalitas: .eyebrow, .tool-badge */

  /* ── Espaciado ──
     Rejilla de 4px. Antes no existía ninguna escala: cada padding, margen y gap era un
     literal suelto (5, 6, 7, 9, 10, 13, 14, 18, 22, 26, 28, 30, 32, 34, 36, 38, 40, 44…),
     lo que hacía imposible que dos componentes distintos respiraran igual. */
  --space-1:  4px;  --space-2:  8px;  --space-3: 12px;  --space-4: 16px;  --space-5: 20px;
  --space-6: 26px;  --space-7: 34px;  --space-8: 44px;  --space-9: 56px;  --space-10: 72px;

  /* ── Ritmo de sección y medida de lectura ──
     El token que más cambia la portada. `.wrap` daba 52px y CADA sección del landing lo
     pisaba con el suyo (44/66, 0/6, 36/8, 36/6, 42/20, 36/14, 30/30, 30/6, 30/12, 14): once
     ritmos verticales distintos, así que el ojo nunca aprendía dónde empieza y dónde termina
     una idea y 2.300 palabras se leían como un rollo continuo. */
  --sec-y:       clamp(52px, 7vw, 88px);
  /* Medida de lectura única: convivían 56ch, 62ch y 66ch sin ninguna razón. */
  --measure:        62ch;
  --measure-narrow: 52ch;
}
/* ── Reset ── */
*, *::before, *::after { box-sizing: border-box; }
/* El `[hidden] { display: none }` de la hoja del navegador tiene especificidad cero, así que
   CUALQUIER regla de autor con `display` le gana: poner `hidden` en un elemento con
   `display: flex` de clase no lo oculta. Es el fallo más silencioso posible —el atributo está
   puesto, el elemento se ve— y en este archivo estaba parcheado quince veces, un selector
   cada vez que alguien tropezaba con él (.field, .grid-2, .notice, .nav-panel, .vak-count,
   .cal-grid…), cada uno con su comentario explicando lo mismo.
   Aquí se arregla una sola vez. El !important es correcto justo aquí: `hidden` significa "no
   está", no "está pero con otro display", y nada debería poder contradecirlo. Los quince
   parches quedan redundantes, no dañinos. */
[hidden] { display: none !important; }
html { overflow-x: hidden; }
/* Desplazamiento suave solo si la usuaria no pidió reducir el movimiento (WCAG 2.3.3). */
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
body {
  margin: 0;
  min-width: 0;
  /* Sin overflow-x aquí: html{overflow-x:hidden} ya recorta el desborde horizontal, y
     un overflow en body convierte a body en contenedor de scroll y ROMPE el header sticky. */
  background: var(--color-background);
  color: var(--color-text);
  font: var(--fs-md)/1.65 var(--font);
  -webkit-font-smoothing: antialiased;
}
/* Grano de papel muy sutil (ruido SVG). Sobre el crema quitaba lo "plano digital" del
   fondo; sobre el blanco actual, a la misma opacidad, se lee como suciedad. Se queda en
   la portada —donde la calidez es el punto— y sale de la herramienta, que es donde hacen
   falta las horas de lectura. `.is-app` lo pone gate.js en <html> al validar la sesión. */
html:not(.is-app) body {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .035 0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}
a { text-decoration: none; color: inherit; }
/* Enlaces dentro de texto corrido: subrayados para no depender solo del color (WCAG 1.4.1).
   Los CTA con clase de botón quedan fuera. */
main p a:not(.btn):not(.outline), .faq-item div a { text-decoration: underline; text-underline-offset: 2px; }
::selection { background: var(--color-accent-soft); color: var(--color-heading); }
img, iframe { max-width: 100%; height: auto; }
/* Fraunces es variable con eje óptico: el navegador elige el diseño según el tamaño
   (más contraste de display en titulares grandes, más robusto en pequeños). */
h1, h2, h3, .logo { font-optical-sizing: auto; }

/* ── Header ── */
.top {
  /* 72 y no 64. La barra lleva seis pestañas con icono, el saldo, la campana y el tema: a
     64px todo eso vivía apretado contra los dos filos, y una barra que se ve estrecha empuja
     ópticamente el contenido hacia abajo aunque el hueco de debajo no cambie. */
  height: 72px;
  padding: 0 4vw;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
  box-shadow: var(--shadow-xs);
  position: sticky;
  top: 0;
  z-index: 100;
}
.logo {
  display: inline-flex;
  align-items: center;
  /* 24px de alto mínimo: es el enlace "volver al inicio" y estaba en 19, por debajo del
     objetivo táctil de WCAG 2.2 (2.5.8). El texto no cambia de tamaño; sólo su caja. */
  min-height: 24px;
  gap: 7px;
  font: 600 21px/1 var(--serif);
  color: var(--color-primary);
  letter-spacing: -.02em;
}
/* La estrella de la marca (misma del favicon) en miel: eco del footer. */
.logo svg { flex: none; color: var(--color-accent-ink); transition: transform .3s ease; }
.logo:hover svg { transform: rotate(45deg); }
.top nav {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
  justify-content: flex-end;
}
/* Excluye .btn/.outline: sin el :not() esta regla gana por especificidad
   sobre .btn y le impone el color apagado y el padding de un enlace. */
/* El icono de cada pestaña de la aplicación. Lo inyecta nav-menu.js; aquí sólo se le da
   sitio y se le quita peso: acompaña a la palabra, no compite con ella. Con seis etiquetas
   parecidas —Planificar, Evaluar, Calendario, Mis cursos, Mis documentos— el icono se
   reconoce antes que el texto, que es para lo que sirve. */
.top nav a:not(.btn):not(.outline) > svg { flex: none; opacity: .75; }
.top nav a[aria-current="page"] > svg,
.top nav a[data-current="true"] > svg { opacity: 1; }
.top nav a:not(.btn):not(.outline) {
  font-size: 13px;
  font-weight: 500;
  color: var(--color-text-secondary);
  padding: 6px 10px;
  border-radius: var(--r-sm);
  min-height: 36px;
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
  transition: color var(--ease), background var(--ease);
}
.top nav a:not(.btn):not(.outline):hover { color: var(--color-primary); background: var(--color-primary-soft); }
.top .logo { margin-right: 12px; }
/* Estado "página actual" en la barra: verde bosque, píldora sage suave y subrayado verde.
   El subrayado era miel. Dos colores para decir una sola cosa —"estás aquí"— obligan a
   aprender que el miel de aquí no es el miel de una tarjeta ni el de la estrella del logo.
   El miel queda para lo que de verdad merece interrumpir; señalar dónde estás no lo es. */
.top nav a[aria-current="page"]:not(.btn):not(.outline) {
  color: var(--color-primary);
  font-weight: 600;
  background: var(--color-primary-soft);
  box-shadow: inset 0 -2px 0 var(--color-primary);
}
/* Grupo de cuenta (Perfil · Planes · Salir) separado de los servicios por una línea fina. */
.top nav .nav-account {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 8px;
  padding-left: 12px;
  border-left: 1px solid var(--color-border);
}
/* ── Barra agrupada + hamburguesa (la construye nav-menu.js) ──
   TODO lo de esta sección cuelga de header.top.is-nav-ready, clase que el script agrega
   sólo si el armado terminó bien: si no carga o falla, queda la barra plana de siempre.
   La sección va DESPUÉS de `.top nav a:not(.btn):not(.outline)` (0,3,2) a propósito: los
   enlaces de panel usan `.top.is-nav-ready nav .nav-panel > a`, que empata en (0,3,2) y
   gana por orden de origen, pero sigue PERDIENDO contra
   `.top nav a[aria-current="page"]:not(…)` (0,4,2) — así la píldora de "página actual"
   se conserva también dentro de los paneles.
   Las reglas con `.top.is-nav-ready` (0,2,x) anulan las de `.top nav` (0,1,x) de los
   @media del final del archivo sin tocarlos: las media queries no aportan especificidad.
   `.top` NUNCA debe llevar overflow:hidden — los paneles y el cajón se anclan a su caja
   con top:100%. */
.top.is-nav-ready { align-items: center; }
.top.is-nav-ready nav { width: auto; justify-content: flex-end; flex-wrap: nowrap; gap: 4px; }
/* El orden visual no depende del orden del DOM: theme.js puede añadir su botón antes o
   después de que este script arme la barra. */
.top.is-nav-ready nav > .nav-list { order: 1; }
.top.is-nav-ready nav > #app-link { order: 2; }
.top.is-nav-ready nav > .nav-plan { order: 3; }
.top.is-nav-ready nav > .nav-bell { order: 4; }
.top.is-nav-ready nav > #theme-toggle { order: 5; }
.top.is-nav-ready nav > .nav-burger { order: 6; }
/* Saldo de créditos: un dato, no una palanca de venta. Va antes de la campana porque se
   consulta de reojo antes de empezar algo que gasta créditos, no cuando algo reclama
   atención. El color del pill ya dice si queda margen, así que no lleva más adorno. */
.top.is-nav-ready nav > .nav-plan { display: inline-flex; align-items: center; margin-left: var(--space-2); }
.top.is-nav-ready nav > .nav-plan[hidden] { display: none; }
.nav-plan .status-pill { transition: border-color var(--ease); }
.nav-plan:hover .status-pill { border-color: var(--color-primary); }
@media (max-width: 700px) {
  /* En móvil compite con la hamburguesa y la campana: sobrevive el número, que es el dato. */
  .top.is-nav-ready nav > .nav-plan { margin-left: var(--space-1); }
  .nav-plan .status-pill { font-size: var(--fs-3xs); padding-left: 6px; padding-right: 6px; }
}
.nav-list, .nav-main { display: flex; align-items: center; gap: 4px; }
/* position:relative en .nav-main es el marco de referencia de .nav-pill. */
.nav-main { position: relative; }
.nav-group { position: relative; z-index: 1; }
.nav-group--account { margin-left: 8px; padding-left: 12px; border-left: 1px solid var(--color-border); }
.nav-trigger {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  /* Posicionado para quedar POR ENCIMA de .nav-pill, que es absoluto y va antes en el DOM. */
  position: relative;
  z-index: 1;
  min-height: 36px;
  padding: 6px 10px;
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--color-text-secondary);
  font: 500 13px/1 var(--font);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--ease), background var(--ease);
}
.nav-trigger:hover, .nav-trigger[aria-expanded="true"] { color: var(--color-primary); background: var(--color-primary-soft); }
/* "Estás en esta sección", que no es lo mismo que "estás en esta página". En parvulos.html
   el tab que hay que encender es "Mis cursos", y ahí aria-current="page" mentiría: la página
   actual no es cursos.html. Por eso un atributo aparte, con el mismo aspecto que el estado
   de página actual — para quien mira son la misma señal, y lo son. Se aplica al disparador
   del grupo de cuenta y a los seis tabs de la app. */
.nav-trigger[data-current="true"],
.top nav a[data-current="true"]:not(.btn):not(.outline) {
  color: var(--color-primary);
  font-weight: 600;
  background: var(--color-primary-soft);
  box-shadow: inset 0 -2px 0 var(--color-primary);
}
.nav-caret { flex: none; width: 12px; height: 12px; transition: transform var(--ease); }
.nav-trigger[aria-expanded="true"] .nav-caret { transform: rotate(180deg); }
.nav-burger {
  display: none; /* sólo ≤960px (ver media query más abajo) */
  flex: none;
  width: 38px;
  height: 38px;
  margin-left: 6px;
  align-items: center;
  justify-content: center;
  border: 1.5px solid var(--color-primary-mid);
  border-radius: var(--r-sm);
  background: var(--color-surface);
  color: var(--color-primary);
  cursor: pointer;
  transition: background var(--ease), border-color var(--ease);
}
.nav-burger:hover { background: var(--color-primary-soft); border-color: var(--color-primary); }
.nav-burger svg { width: 18px; height: 18px; }
/* Un `display` de clase le gana al `[hidden]{display:none}` de la hoja del navegador:
   sin la segunda regla el atributo no ocultaría nada (mismo caso que .vak-count[hidden]). */
.nav-panel { display: flex; flex-direction: column; gap: 2px; }
.nav-panel[hidden] { display: none; }
/* Descendiente y no hijo directo (misma especificidad (0,3,2)): en los paneles de varias
   columnas los enlaces cuelgan de un .nav-col, no del panel. */
.top.is-nav-ready nav .nav-panel a,
.top.is-nav-ready nav .nav-panel .outline {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  min-height: 40px;
  padding: 9px 12px;
  border: 0;
  border-radius: var(--r-sm);
  background: none;
  color: var(--color-text);
  font: 500 13.5px/1.35 var(--font);
  text-align: left;
  white-space: normal;
  cursor: pointer;
}
.top.is-nav-ready nav .nav-panel a:hover,
.top.is-nav-ready nav .nav-panel .outline:hover { background: var(--color-primary-soft); color: var(--color-primary); }
/* Enlaces sueltos de la barra: también por encima de la píldora (ver .nav-trigger). */
.top.is-nav-ready nav .nav-main > a { position: relative; z-index: 1; }
/* ── La línea entre destinos ──
   Sustituye al icono que llevaba cada pestaña. Seis iconos de trazo fino, todos del mismo
   tamaño y del mismo gris, no se distinguían entre sí de un vistazo: sólo metían una mancha
   entre palabra y palabra. Lo que hacía falta era decir dónde termina un destino y empieza el
   siguiente, y para eso una línea de 1px hace el trabajo sin competir con el texto.
   No es un recurso nuevo: es el mismo filete con el que la barra ya separaba el grupo de
   cuenta del resto, ahora también entre pestañas.
   Va en un ::before absoluto y NO como border-left del enlace, porque cada ítem tiene su
   píldora de fondo —la de hover y la de "estás aquí"—: un borde propio se pintaría dentro de
   esa píldora y la partiría. Así la línea vive en el hueco de 4px del gap, fuera de las dos.
   `right: calc(-1 * ...)` en vez de `left` sobre el hermano siguiente: el separador cuelga del
   ítem de la IZQUIERDA, que es el que siempre existe cuando hay dos. */
.top.is-nav-ready nav .nav-main > a + a::before {
  content: '';
  position: absolute;
  left: -2.5px;
  top: 50%;
  transform: translateY(-50%);
  width: 1px;
  height: 17px;
  background: var(--color-border);
}
@media (max-width: 960px) {
  /* En el cajón los destinos van en columna: ahí la separación la da el propio apilado, y una
     línea vertical entre dos filas no separaría nada. */
  .top.is-nav-ready nav .nav-main > a + a::before { display: none; }
}

/* ── Fichas del panel: icono + título + descripción ──
   Este selector suma (0,4,2) y EMPATA con `.top nav a[aria-current="page"]:not(…)`,
   ganándole por orden de aparición. Por eso aquí sólo va composición (rejilla, caja):
   ni color ni fondo, o la píldora de "página actual" desaparecería dentro del panel. */
.top.is-nav-ready nav .nav-panel a.nav-item {
  display: grid;
  grid-template-columns: 34px 1fr;
  align-items: center;
  gap: 10px;
  min-height: 46px;
  padding: 8px 10px;
}
.nav-item__ico {
  display: grid;
  place-content: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--color-border);
  border-radius: var(--r-sm);
  background: var(--color-background);
  color: var(--color-primary);
  transition: background var(--ease), border-color var(--ease), color var(--ease);
}
.nav-item:hover .nav-item__ico { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-on-accent); }
.nav-item__txt { display: block; min-width: 0; }
.nav-item__txt b { display: block; font-weight: 600; font-size: 13.5px; line-height: 1.3; }
/* --color-text-secondary y no --color-text-tertiary: a 12px hace falta 4.5:1 (WCAG 1.4.3) y --color-text-tertiary se queda en
   3.1:1 sobre crema y 4.2:1 sobre el fondo oscuro. --color-text-secondary da 5.4:1 y 7.1:1. */
.nav-item__txt small { display: block; margin-top: 2px; font-size: 12px; line-height: 1.35; color: var(--color-text-secondary); }
.nav-item:hover .nav-item__txt small { color: var(--color-text); }
/* Panel ancho de varias columnas. */
.nav-panel--mega .nav-cols { display: flex; align-items: flex-start; gap: 24px; }
/* flex-basis auto (y no un ancho fijo): así el `width: max-content` del panel se calcula
   sobre el texto real de las fichas y ningún título se parte en dos líneas. */
.nav-col { display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-width: 0; }
.nav-col > h3 {
  margin: 6px 0 4px 10px;
  font: 700 10.5px/1 var(--font);
  letter-spacing: .08em;
  text-transform: uppercase;
  /* 10.5px no califica como "texto grande" ni en negrita: mismo criterio que las
     descripciones, --color-text-secondary en vez de --color-text-tertiary. */
  color: var(--color-text-secondary);
}
/* Píldora que sigue al puntero entre los ítems (la mueve nav-menu.js midiendo cajas).
   Va detrás de todo: el fondo de :hover de cada ítem se pinta encima y es el estado real. */
.nav-pill {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 0;
  border-radius: var(--r-sm);
  background: var(--color-primary-soft);
  opacity: 0;
  pointer-events: none;
  transition: transform .22s cubic-bezier(.4, 0, .2, 1), width .22s cubic-bezier(.4, 0, .2, 1),
              height .22s cubic-bezier(.4, 0, .2, 1), opacity .16s ease;
}
.nav-pill.is-on { opacity: 1; }
/* Entrada del panel. Sólo si no se pidió reducir el movimiento. */
@media (prefers-reduced-motion: no-preference) {
  .top.is-nav-ready .nav-panel:not([hidden]) { animation: nav-panel-in 160ms cubic-bezier(.2, .7, .3, 1) both; }
}
@keyframes nav-panel-in { from { opacity: 0; transform: translateY(-6px); } }
@media (min-width: 961px) {
  .top.is-nav-ready .nav-panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    /* z-index local: `.top` ya crea contexto de apilamiento (sticky + z-index:100), así
       que este 10 sólo compite con sus hermanos y no interfiere con .skip-link (200)
       ni .combo-list (300), que se comparan contra `.top` en la raíz. */
    z-index: 10;
    min-width: 232px;
    max-width: min(320px, calc(100vw - 32px));
    padding: 6px;
    background: var(--color-surface);
    /* Borde visible: en oscuro la sombra casi no lee sobre el fondo. */
    border: 1.5px solid var(--color-border);
    border-radius: var(--r-sm);
    box-shadow: var(--shadow-lg);
  }
  /* `width: max-content` es imprescindible, no cosmético: el panel es absoluto y su bloque
     contenedor es el .nav-group (apenas el ancho del disparador), así que el shrink-to-fit
     lo estruja al min-width de 232px y cada ficha parte el texto en tres líneas.
     Los max-width siguen acotados a la ventana para que is-flip pueda reubicarlos. */
  .top.is-nav-ready .nav-panel--cards {
    width: max-content;
    max-width: min(340px, calc(100vw - 32px));
    padding: 10px;
    border-radius: var(--r);
  }
  /* Después de --cards a propósito: el panel de columnas hereda el ancho natural de ahí
     y sólo sube el tope, el aire y el radio. */
  .top.is-nav-ready .nav-panel--mega {
    /* Tres columnas (Planificar / Evaluar / Recursos), el mismo reparto 3-3-3 que usan la
       portada y servicios.html. Con dos cabía en 600px; con tres necesita 860. */
    max-width: min(860px, calc(100vw - 32px));
    padding: 14px;
    border-radius: var(--r-lg);
  }
  .nav-panel--mega .nav-cols { padding-bottom: 10px; margin-bottom: 6px; border-bottom: 1px solid var(--color-border); }
  /* html{overflow-x:hidden} recorta lo que se sale por la derecha en vez de permitir
     scroll: los grupos del extremo se abren hacia adentro. */
  .top.is-nav-ready .nav-group--account .nav-panel,
  .top.is-nav-ready .nav-panel.is-flip { left: auto; right: 0; }
}
@media (max-width: 960px) {
  .top.is-nav-ready .nav-burger { display: inline-flex; }
  .top.is-nav-ready .nav-list {
    display: none; /* cerrado ⇒ fuera del orden de tabulación, sin inert ni aria-hidden */
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 10;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    max-height: calc(100vh - 60px);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 10px 5vw 16px;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    box-shadow: var(--shadow-lg);
  }
  /* Cuatro clases (0,4,0): debe superar a `.top.is-nav-ready .nav-list{display:none}` (0,3,0). */
  .top.is-nav-ready.is-nav-open .nav-list { display: flex; }
  .top.is-nav-ready .nav-main { flex-direction: column; align-items: stretch; gap: 2px; }
  .top.is-nav-ready .nav-list > *, .top.is-nav-ready .nav-main > * { width: 100%; }
  .top.is-nav-ready .nav-trigger { width: 100%; justify-content: space-between; min-height: 44px; font-size: 14px; }
  .top.is-nav-ready nav .nav-main > a { min-height: 44px; padding: 10px 12px; font-size: 14px; }
  .top.is-nav-ready .nav-panel { margin-left: 10px; padding: 2px 0 6px 12px; border-left: 2px solid var(--color-primary-mid); }
  .top.is-nav-ready nav .nav-panel a,
  .top.is-nav-ready nav .nav-panel .outline { min-height: 44px; }
  .top.is-nav-ready .nav-group--account { margin: 8px 0 0; padding: 8px 0 0; border-left: 0; border-top: 1px solid var(--color-border); }
  /* En el cajón las columnas se apilan y las descripciones se ocultan: si no, el menú
     mide varias pantallas de alto y hay que hacer scroll para llegar a "Ingresar". */
  .nav-panel--mega .nav-cols { flex-direction: column; gap: 0; }
  .nav-col > h3 { margin-top: 10px; }
  .top.is-nav-ready .nav-item__txt small { display: none; }
}
@media print {
  .top.is-nav-ready .nav-burger, .top.is-nav-ready .nav-panel { display: none !important; }
}

.footer-links { display: flex; flex-wrap: wrap; gap: 2px 10px; align-items: center; justify-content: center; }
/* Los cinco enlaces del pie medían 20px de alto. Están separados por "·", así que técnicamente
   les valdría la excepción de enlace en línea de WCAG 2.5.8, pero en un teléfono son cinco
   objetivos pequeños y contiguos: se les da la caja de 24px igual. */
.footer-links a { display: inline-flex; align-items: center; min-height: 24px; padding: 0 var(--space-1); }
.footer-note { margin-top: 14px; color: var(--color-text-tertiary); }

/* ── Main layout ── */
/* Un solo ritmo vertical para todo el sitio. Las secciones del landing lo pisaban una a
   una con su propio par de valores; ahora sólo quedan DOS excepciones, y ambas están
   justificadas más abajo: `.landing-hero` y `.trustbar`. */
.wrap {
  max-width: 1300px;
  margin: 0 auto;
  padding: var(--sec-y) 4vw;
}
/* ── El aire de arriba, en la aplicación, no es el del sitio público ──
   --sec-y da 88px en escritorio, y para la portada está bien: ahí ese hueco es lo que separa
   una idea de la siguiente en una página que se LEE. Pero las páginas de la aplicación se
   USAN, y todas empiezan igual —barra, un hueco enorme, el título—, así que en cada una de las
   veintiocho lo primero que hace la pantalla es no decir nada durante 88px. El contenido
   arrancaba por debajo de la mitad del alto útil en un portátil.
   Se recorta a la mitad sólo aquí (html.is-app lo marca gate.js), que es exactamente el mismo
   corte que ya usa el CSS de impresión para distinguir las dos mitades del producto. El hueco
   de DEBAJO del título no se toca: ése separa el título de su contenido y sigue haciendo
   falta. */
html.is-app .wrap { padding-top: var(--space-7); }

/* ── Buttons ── */
.btn, .outline {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  /* De 44px de alto a 50, y de 14px de texto a 15. El de ellos mide 60 y 18: la accion
     principal de cada pantalla usa .btn--lg, mas abajo, y llega ahi. */
  padding: 13px 24px;
  border-radius: var(--r);
  font: 600 15px/1.2 var(--font);
  cursor: pointer;
  border: none;
  transition: background var(--ease), color var(--ease), box-shadow var(--ease), transform var(--ease);
  text-align: center;
  white-space: nowrap;
  max-width: 100%;
  min-height: 40px;
}
.btn {
  background: var(--color-primary);
  color: var(--color-on-accent);
  border: 1px solid var(--color-primary-active);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.18);
}
.btn:hover { background: var(--color-primary-hover); box-shadow: inset 0 1px 0 rgba(255,255,255,.18), var(--shadow-sm); }
.btn:active { background: var(--color-primary-active); transform: translateY(1px); }
.outline {
  background: var(--color-surface);
  color: var(--color-primary);
  border: 1.5px solid var(--color-primary-mid);
}
.outline:hover { background: var(--color-primary-soft); border-color: var(--color-primary); color: var(--color-primary-ink); }

/* Variantes de IA. Van DESPUÉS de .btn:hover y .outline:hover a propósito: .btn--ai tiene
   especificidad (0,1,0) y .btn:hover (0,2,0), así que sin su propia regla de hover el botón
   violeta se volvería turquesa al pasar el puntero. No lo caza ningún test —sólo se ve con
   el ratón encima— y por eso queda escrito aquí. */
/* La acción principal de una pantalla: 58px de alto y 17px de texto, en píldora. No es
   capricho de tamaño — es que en una pantalla con quince campos tiene que haber UNA cosa
   que se vea desde lejos, y hoy el botón de generar medía lo mismo que "Duplicar". */
.btn--lg, .outline--lg { padding: 18px 34px; font-size: 17px; border-radius: var(--r-pill); }
.btn--lg::before, .outline--lg::before { width: 16px; height: 16px; }

.btn--ai { background: var(--color-ai); border-color: var(--color-ai-hover); }
.btn--ai:hover { background: var(--color-ai-hover); }
.btn--ai:active { background: var(--color-ai-active); }
.outline--ai { color: var(--color-ai-ink); border-color: var(--color-ai); }
.outline--ai:hover { background: var(--color-ai-soft); border-color: var(--color-ai-ink); }
/* La caja ya es inline-flex con gap, así que la chispa se coloca sola delante de la etiqueta. */
.btn--ai::before, .outline--ai::before, .plan-saldo::before, .nav-plan .status-pill::before {
  content: '';
  flex: none;
  width: 13px;
  height: 13px;
  background: currentColor;
  -webkit-mask: var(--icon-spark) center / contain no-repeat;
  mask: var(--icon-spark) center / contain no-repeat;
}
.outline:active { transform: translateY(1px); }
.btn:disabled, .outline:disabled {
  opacity: .45;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
/* Variante peligro para acciones destructivas (eliminar cuenta).
   Los cinco valores estaban escritos a mano aquí y además declarados en :root sin que nadie
   los usara. Ahora pasan por el token, que es la única lista de valores del archivo. */
.btn.danger { background: var(--color-danger); }
.btn.danger:hover { background: var(--color-danger-hover); }
.outline.danger { color: var(--color-danger); border-color: var(--color-danger-border); }
.outline.danger:hover { background: var(--color-danger-soft); border-color: var(--color-danger); }
.danger-zone { margin-top: 20px; border-color: var(--color-danger-border); }
.danger-confirm { margin-top: 16px; border-top: 1px dashed var(--color-danger-border); padding-top: 16px; }

/* ── Cards & panels ── */
.card, .panel {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--r-lg);
  padding: 28px 32px;
  box-shadow: var(--shadow);
  overflow: hidden;
}
/* Jerarquía: el panel es un contenedor calmo; la card es la pieza con carácter.
   La esquina inferior-izquierda "doblada" (radio 4px) es la firma de las cards. */
.panel { box-shadow: var(--shadow-xs); }
/* `overflow: hidden` recorta las esquinas, pero de paso convierte al panel en contenedor de
   scroll, y eso anula cualquier `position: sticky` de dentro: la barra de acción del
   formulario se quedaba quieta en mitad de la página. `clip` recorta igual SIN crear el
   contenedor de scroll, así que el sticky vuelve a mirar al viewport. La declaración
   anterior queda como respaldo para los navegadores que no conocen `clip`: ahí la barra es
   una barra normal, que era el comportamiento de siempre. */
.panel { overflow: clip; }
/* `.card, .panel` no declaraba margen, así que dos paneles seguidos —el caso de TODAS las
   fichas de herramienta, que llevan "Qué recibes" y "Cómo funciona" una debajo de otra— se
   tocaban: dos cajas con borde pegadas se leen como una caja rota, no como dos secciones.
   ayuda.html lo tapaba con un style="margin-top:24px" propio; ese parche ya se retiró. */
.panel + .panel, .panel + .ejemplo-cta, .panel + .tool-related { margin-top: var(--space-6); }
/* La tarjeta va en el plano elevado, no en el del panel que la contiene. Antes ambos usaban
   --color-surface, así que una tarjeta dentro de un panel estaba a 1.000 de separación: el mismo
   color exacto, y el único contorno era un borde de 1.34 que no se leía. */
.card { background: var(--color-card); border-radius: var(--r-lg) var(--r-lg) var(--r-lg) 4px; }
a.card { transition: transform var(--ease), box-shadow var(--ease), border-color var(--ease); }
/* ── El efecto al pasar existía y no se veía ──
   Las tres señales se disparaban —lo medido con el puntero encima: translateY(-2px), sombra
   larga y borde cambiado— y ninguna llegaba al ojo. Dos de las tres se apagaron con el papel
   de lino, y eso se ve midiendo, no mirando:
     · El borde pasaba de #CDCBB8 a --color-primary-mid #BFD3B8. Entre esos dos hay 1.03 de
       diferencia: 1.00 es el MISMO color exacto. O sea, el borde no cambiaba.
     · La sombra sube de .10 a .16 de alfa repartida sobre 40px de desenfoque. Sobre un blanco
       frío se leía; sobre lino, un tinte cálido tan diluido no separa la tarjeta del papel.
     · Y 2px de elevación son 2px.
   Ahora el borde va al primario —3.64 de diferencia contra el de reposo, no 1.03—, que es lo
   que esta hoja ya usa para decir "esto responde" en un campo o en una etiqueta. Y la
   elevación sube a los 4px que ya usan .trust-card y .tool-card: es el valor del sistema, no
   uno nuevo. Con esas dos, la sombra deja de cargar sola con el trabajo. */
a.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); border-color: var(--color-primary); }
@media (prefers-reduced-motion: reduce) {
  /* Sin desplazamiento, pero el borde y la sombra se quedan: quien pide menos movimiento no
     está pidiendo menos información. */
  a.card { transition: box-shadow var(--ease), border-color var(--ease); }
  a.card:hover { transform: none; }
}

/* ── La cinta ──
   La tarjeta queda PEGADA al papel, no flotando. Es el mismo recurso que la portada usa en su
   documento de muestra (.doc-preview::after) desde hace tiempo; aquí se reutiliza el gesto,
   no se inventa un segundo.
   VA SÓLO EN LA APLICACIÓN (html.is-app, la marca que pone gate.js). En la portada la cinta
   es un momento único —una pieza, arriba, la primera que se ve—; repetirla en las nueve
   fichas de servicios.html no la extendería, la gastaría.
   Fuera de los esqueletos: una cinta de color sobre un bloque gris de carga se lee como que
   el contenido ya llegó.
   No lleva margen extra. La cinta sobresale 9px y las rejillas tienen 16 de hueco, así que
   cabe entera sin empujar nada; y dentro de un panel hay 28px de relleno por encima, de sobra
   para que el `overflow: clip` del panel no la recorte. */
html.is-app .card { position: relative; }
html.is-app .card:not(.card--sk)::before {
  content: '';
  position: absolute;
  top: -9px;
  left: 50%;
  width: 66px;
  height: 18px;
  transform: translateX(-50%) rotate(-1.5deg);
  background: var(--color-accent-soft);
  border: 1px solid var(--color-accent);
  border-radius: 2px;
  box-shadow: var(--shadow-xs);
}
/* La misma cinta, en el mismo sitio exacto, siete veces en una pantalla se lee como una
   calcomanía de plantilla — que es justo lo contrario de lo que la cinta viene a decir. Nadie
   pega siete trozos idénticos y centrados al milímetro. Tres variantes de giro y desplazamiento
   bastan para que el ojo lea "puesto a mano" y no "generado": es el mismo recurso de
   :nth-child con el que las rejillas de la portada rompen la uniformidad de sus filos. */
html.is-app .card:nth-child(3n+2):not(.card--sk)::before { transform: translateX(-56%) rotate(1.2deg); width: 58px; }
html.is-app .card:nth-child(3n+3):not(.card--sk)::before { transform: translateX(-44%) rotate(-.6deg); width: 72px; }
/* Acentos rotativos: rompe la uniformidad "template" de las grillas de cards.
   NO se aplican en una grilla `--plain`, que es la que se filtra. El acento sale de
   :nth-child, así que al escribir en el buscador las tarjetas se recolocan y CAMBIAN de
   color: el mismo recurso es verde, luego miel, luego terracota. Como decoración en una
   página de presentación funciona; sobre una lista que se filtra, el color parece
   significar algo y no significa nada.
   Esa advertencia estaba escrita aquí y aun así la regla se aplicaba en toda la app. La
   escribí para la portada y se filtró: `.grid--plain` hay que acordarse de ponerlo, y en
   app.html, perfil.html, suscripcion.html, cobertura.html y admin.html nadie se acordó.
   Ahora el reparto es al revés —la portada opta por el color, la herramienta no lo tiene—
   y no depende de la memoria de nadie: `.is-app` lo pone gate.js al validar la sesión.
   Dentro de la app, tres tarjetas seguidas con tres bordes de colores distintos son tres
   promesas de significado que ninguna cumple. */
html:not(.is-app) .grid:not(.grid--plain) > .card:nth-child(3n+1) { border-top: 2px solid var(--color-primary); }
html:not(.is-app) .grid:not(.grid--plain) > .card:nth-child(3n+2) { border-top: 2px solid var(--color-accent); }
html:not(.is-app) .grid:not(.grid--plain) > .card:nth-child(3n)   { border-top: 2px solid var(--color-coral); }
.card h2, .card h3, .panel h1, .panel h2 {
  font-family: var(--serif);
  font-weight: 540;
}
.panel h2 { font-size: var(--fs-2xl); margin: 4px 0 var(--space-4); color: var(--color-heading); }
.card h2  { font-size: var(--fs-xl); margin: var(--space-2) 0 var(--space-2); color: var(--color-heading); }
.card h3  { font-size: var(--fs-lg); margin: var(--space-2) 0 var(--space-2);  color: var(--color-heading); }
.panel h1 { font-size: var(--fs-3xl); margin: 4px 0 var(--space-4); color: var(--color-heading); }

/* ── Grids ── */
.grid   { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr;       gap: var(--space-5); }
/* Igual que .field[hidden]: un display de clase le gana al [hidden]{display:none} del
   navegador. Sin esto, la fila de Nivel/Edad seguiría ocupando su hueco cuando el curso ya
   los sabe, que es justo el vacío que había que quitar. */
.grid-2[hidden] { display: none; }
.grid > *, .grid-2 > * { min-width: 0; }

/* ── Eyebrow labels ── */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-3xs);
  font-weight: var(--w-label);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--color-primary);
  margin-bottom: 2px;
}
/* Guión miel: pequeña marca editorial antes del kicker. */
.eyebrow::before { content: ''; flex: none; width: 16px; height: 2px; border-radius: 1px; background: var(--color-accent); }

/* ── Page title ── */
.page-title { margin-bottom: var(--space-7); }
.page-title h1, .page-title h2 {
  font: 530 34px/1.1 var(--serif);
  color: var(--color-heading);
  margin: 6px 0 10px;
  text-wrap: balance;
}
.page-title p { color: var(--color-text-secondary); font-size: var(--fs-md); line-height: 1.6; max-width: var(--measure); }

/* ── El encabezado de una pantalla de la aplicación ──
   Grande y centrado. Es la diferencia más visible entre nuestra aplicación y la de
   planificada.cl: allí cada pantalla se abre con su nombre a ~44px en el centro y UNA línea
   que dice para qué sirve, y eso ordena la vista antes de que empiece el primer campo.
   Aquí el mismo encabezado medía 34px pegado a la izquierda, así que la pantalla empezaba
   sin decir dónde estabas.
   Sólo dentro de la app (html.is-app, lo marca gate.js): en las páginas públicas
   .page-title es el título de una sección dentro de una composición que ya tiene su propio
   ritmo, y centrarlo ahí descuadraría el hero. */
/* Los encabezados de sección de la portada NO viven todos en .page-title: cuatro son un
   <h2> suelto con su propia clase, y centrar sólo los que estaban dentro dejó la página a
   dos alineaciones, que es peor que las nueve a la izquierda que había antes. Se centran
   todos. Se queda fuera la banda de cierre, que es una composición a dos columnas —texto a
   la izquierda, botón a la derecha— igual que el hero. */
.compare > h2, .steps-title, .trust-title { text-align: center; margin-left: auto; margin-right: auto; max-width: 22ch; }
.page-title { text-align: center; margin-bottom: var(--space-8); }
.page-title h1,
.page-title h2 { font-size: clamp(30px, 4.2vw, 44px); margin: var(--space-2) 0 var(--space-3); }
/* La línea de debajo se estrecha y se centra: una medida de lectura larga centrada se sigue
   con dificultad, así que baja de 62ch a 54ch. */
.page-title p { max-width: 54ch; margin-left: auto; margin-right: auto; }
/* Las pestañas de sección van con el título, no pegadas al margen. */
html.is-app .subnav { justify-content: center; }

/* ── Form fields ── */
.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 14px 0;
  /* La pregunta de un campo estaba en 12px —el tamaño de las micro-notas y los pies de
     documento— mientras la respuesta que se escribe dentro va en 14. Un rótulo más pequeño
     que su propio contenido se lee como un pie de foto, no como la pregunta, y en un
     formulario de quince campos la pregunta es lo único que hay que poder barrer de un
     vistazo. Cuando la escala entera subió un escalón, ésta se quedó atrás. */
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--color-text);
  letter-spacing: .01em;
}
.field input, .field select, .field textarea {
  padding: 10px 14px;
  /* El borde fuerte y no el normal: 1.4.11 pide 3:1 para el contorno de un control, y
     ninguno de los dos llega solo, así que el campo se distingue por fondo hundido MÁS
     borde. Medido en la página: con el borde normal daba 1.52 sobre la tarjeta y 1.28 sobre
     su propio relleno; con éste, 2.08 y 1.75. */
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--r-sm);
  font: 14px/1.5 var(--font);
  background: var(--color-surface-sunken);
  color: var(--color-text);
  transition: border-color var(--ease), box-shadow var(--ease), background var(--ease);
  width: 100%;
}
/* El navegador NO pone el cursor de mano en un <select>, así que un desplegable se
   comportaba como un trozo de texto hasta que lo pulsabas. Los campos de texto conservan el
   cursor de escritura, que ahí es la señal correcta. */
.field select { cursor: pointer; }
.field select:hover { border-color: var(--color-primary); }

/* ── El desplegable abierto ──
   El <select> cerrado siempre se pudo vestir; la LISTA que se abre al pulsarlo, no: la pinta
   el sistema operativo, con su tipo de letra, su gris y su altura de fila. Por eso "Establecimiento"
   se veía cuidado hasta que lo abrías y aparecía un menú de Windows con cinco opciones
   apretadas.
   `appearance: base-select` (Chromium 135+) convierte el select en un elemento estilable de
   verdad, con su lista en `::picker(select)` y sus `option` como cajas normales. Va DENTRO de
   un @supports y no suelto: donde no exista —Firefox y Safari, hoy— no se aplica nada y queda
   el desplegable nativo entero, que es lo correcto. Un desplegable a medias, con la caja
   vestida y la lista del sistema debajo, sería peor que el de ahora.
   Sigue siendo un <select> de verdad: el navegador conserva el teclado, el lector de pantalla
   y el comportamiento táctil. No se sustituye por un menú de <div>, que es donde se pierden
   esas tres cosas a cambio de un borde redondeado. */
@supports (appearance: base-select) {
  .field select,
  .field select::picker(select) { appearance: base-select; }
  /* La caja cerrada conserva su aspecto; sólo hay que colocar el contenido, porque en este
     modo el select pasa a ser un contenedor flex con la flecha como hijo. */
  .field select { justify-content: space-between; gap: var(--space-3); text-align: left; }
  /* El triángulo relleno del navegador se cambia por el galón del sitio: es el mismo trazo
     que abre las preguntas frecuentes y el ejemplo del documento, así que "esto se despliega"
     se dice igual en todas partes. El glifo propio se apaga con color:transparent y el dibujo
     lo pone la máscara, el mismo patrón que la chispa de los créditos. */
  .field select::picker-icon {
    flex: none;
    width: 13px; height: 13px;
    color: transparent;
    background: var(--color-text-secondary);
    -webkit-mask: var(--icon-caret) center / contain no-repeat;
    mask: var(--icon-caret) center / contain no-repeat;
    transition: transform var(--ease);
  }
  .field select:hover::picker-icon { background: var(--color-primary); }
  .field select:open::picker-icon { transform: rotate(180deg); }
  @media (prefers-reduced-motion: reduce) { .field select::picker-icon { transition: none; } }
  /* La lista es una tarjeta, con el mismo lenguaje que el panel de avisos o un diálogo: papel
     elevado, borde fino, sombra larga. Y con tope de alto: un curso con veinte opciones no
     puede abrir una lista de la altura de la pantalla. */
  .field select::picker(select) {
    padding: 6px;
    border: 1px solid var(--color-border);
    border-radius: var(--r-sm);
    background: var(--color-card);
    box-shadow: var(--shadow-lg);
    max-height: 320px;
    overflow-y: auto;
  }
  .field select option {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 9px 11px;
    border-radius: 8px;
    font: 500 13.5px/1.35 var(--font);
    color: var(--color-text);
  }
  /* 44px de alto en el dedo: en un teléfono estas listas se tocan, y 2.5.5 pide ese objetivo. */
  @media (pointer: coarse) { .field select option { min-height: 44px; } }
  .field select option:hover { background: var(--color-surface-sunken); }
  /* La elegida se marca con el tinte y el peso, no sólo con el tilde: el tilde es un glifo de
     14px y el color por sí solo tampoco basta (1.4.1). */
  .field select option:checked {
    background: var(--color-primary-soft);
    color: var(--color-primary-ink);
    font-weight: var(--w-strong);
  }
  .field select option:checked::checkmark { color: var(--color-primary); }
  .field select option:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }
}
.field input::placeholder, .field textarea::placeholder { color: var(--color-text-tertiary); }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  background: var(--color-surface);
  box-shadow: 0 0 0 3px rgb(var(--color-primary-rgb) / .12);
}
.field textarea { min-height: 100px; resize: vertical; }
fieldset.field { border: 1.5px solid var(--color-border); border-radius: var(--r-sm); padding: 14px 16px; }
fieldset.field legend { font-size: var(--fs-sm); font-weight: 600; padding: 0 6px; }

/* ── Searchable combobox (OAT) ── */
.combo { position: relative; }
.combo-input:disabled { background: var(--color-surface-sunken); color: var(--color-text-tertiary); cursor: not-allowed; }
.combo-list {
  list-style: none; margin: 5px 0 0; padding: 4px;
  position: absolute; left: 0; right: 0; top: 100%; z-index: 300;
  max-height: 280px; overflow-y: auto;
  background: var(--color-surface); border: 1.5px solid var(--color-border);
  border-radius: var(--r-sm); box-shadow: var(--shadow-lg);
}
.combo-group { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--color-text-tertiary); padding: 9px 10px 4px; }
.combo-option, .combo-empty { padding: 8px 10px; border-radius: 4px; font-size: 13px; font-weight: 500; color: var(--color-text); cursor: pointer; line-height: 1.4; }
.combo-empty { color: var(--color-text-secondary); font-style: italic; }
.combo-option:hover, .combo-option.is-active, .combo-empty.is-active { background: var(--color-primary-soft); color: var(--color-primary); }
.combo-option[aria-selected="true"] { font-weight: 700; color: var(--color-primary); }
.combo-noresult { padding: 10px; font-size: 13px; color: var(--color-text-secondary); }

/* ── El objetivo de aprendizaje: un campo, no tres ──
   Sustituye la cascada Ámbito → Núcleo → OA. Es la decisión que define el documento
   entero, así que ocupa el ancho completo y lleva un cuerpo mayor que el resto: la
   jerarquía visual tiene que decir cuál es LA elección y cuáles son los acompañantes. */
.oa-field { margin: var(--space-4) 0; }
.oa-field .combo-input { font-size: var(--fs-md); padding: 12px 14px; }
/* Una lupa dentro del campo. Sin ella el buscador de objetivos parecía una caja de texto
   vacía: nada decía que ahí se escribe para BUSCAR, y menos aún cuando está deshabilitado
   esperando el nivel. El icono va en el fondo, no en el HTML, para no meter un nodo que los
   lectores de pantalla tendrían que saltarse. */
.combo--search .combo-input {
  padding-left: 40px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234B5563' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.5-3.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 13px center;
  background-size: 17px 17px;
}
:root[data-theme="dark"] .combo--search .combo-input {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23A7B0BD' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.5-3.5'/%3E%3C/svg%3E");
}
/* El rastro de dónde salió el objetivo. Es lo único que aportaban los dos desplegables
   que se retiraron, así que se conserva —como dato, no como pregunta. */
.oa-crumb { font-size: 11px; font-weight: 600; letter-spacing: .04em; color: var(--color-text-secondary); text-transform: uppercase; }
.oa-crumb[hidden] { display: none; }
/* `.field` fija display:flex y le gana al [hidden]{display:none} del navegador: sin esta
   regla, ocultar Nivel y Edad cuando el curso ya los sabe no ocultaría nada. Mismo caso
   que .nav-panel[hidden] y .vak-count[hidden]. */
.field[hidden] { display: none; }

/* ── Ajustes plegados ──
   Todo lo que la IA sabe proponer sola vive aquí: tipo, duración, espacio, modalidad,
   recursos, establecimiento, OAT y comentario. Antes eran nueve campos siempre a la
   vista compitiendo por atención con los tres que de verdad hay que responder. */
/* El plegable vive DENTRO de un panel, así que con el fondo del panel quedaba sin contorno:
   el mismo caso que las tarjetas. Va en el plano del papel. */
.ajustes { border: 1.5px solid var(--color-border-strong); border-radius: var(--r-sm); margin: 16px 0; background: var(--color-surface-sunken); }
.ajustes > summary { position: relative; padding: 13px 44px 13px 16px; cursor: pointer; display: flex; flex-direction: column; gap: 3px; list-style: none; border-radius: var(--r-sm); }
.ajustes > summary::-webkit-details-marker { display: none; }
.ajustes > summary:hover { background: var(--color-card); }
.ajustes:hover { border-color: var(--color-primary); }
.ajustes > summary:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }
.ajustes > summary::after {
  content: ''; position: absolute; right: 18px; top: 18px;
  width: 8px; height: 8px; border-right: 2px solid var(--color-text-secondary); border-bottom: 2px solid var(--color-text-secondary);
  transform: rotate(45deg); transition: transform var(--ease);
}
.ajustes[open] > summary::after { transform: rotate(-135deg) translate(-3px, -3px); }
.ajustes-title { font-size: 13px; font-weight: 700; color: var(--color-text); }
.ajustes-hint { font-size: 12px; font-weight: 400; color: var(--color-text-secondary); line-height: 1.45; }
/* ── El cuerpo que se abre NO es la superficie que se pulsa ──
   `.ajustes` va hundido porque se pulsa —hundido = te responde, la regla de esta hoja—, y sus
   seis campos van hundidos por lo mismo. Los dos a la vez daban 1.000: un campo del mismo
   color exacto que su caja, que es como "Ajustes de la experiencia" acabó pareciendo fondo
   pintado en vez de seis controles.
   Lo que se pulsa es el SUMMARY, no el cuerpo. Así que el cuerpo sube al plano del papel y
   los campos vuelven a hundirse contra algo: 1.274. Ninguna de las dos reglas del sistema se
   toca — se separa lo que nunca debió compartir plano. */
.ajustes-body { padding: 14px 16px; background: var(--color-card); border-top: 1px solid var(--color-border-strong); }

/* ── Apoyos por vía de acceso VAK (Decreto N.° 83) ── acordeón compacto ── */
.support-intro { margin: 0 0 10px; }
/* ── Las tres promesas de la portada ──
   Antes eran nueve fichas de herramienta con su costo en créditos. Las nueve siguen
   existiendo —en servicios.html, que es donde se explican—, pero la portada ya no obliga a
   leerlas para entender qué hace el producto: promete tres resultados.
   El icono va en un cuadro redondeado de color, que es lo que hace que una tarjeta se lea
   como una idea y no como un párrafo con un adorno delante. */
.promesas { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--space-5); }
.promesa {
  background: var(--color-card);
  border: 1px solid var(--color-border);
  border-radius: var(--r-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
}
.promesa-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: var(--r);
  background: var(--color-primary-soft);
  color: var(--color-primary-ink);
  margin-bottom: var(--space-4);
}
/* Cada promesa con su tinte, para que las tres se distingan de un vistazo sin leerlas. El
   segundo usa el violeta porque evaluar e informar es lo que más gasta créditos. */
.promesa:nth-child(2) .promesa-badge { background: var(--color-ai-soft); color: var(--color-ai-ink); }
.promesa:nth-child(3) .promesa-badge { background: var(--color-accent-soft); color: var(--color-accent-ink); }
.promesa h3 { font: var(--w-sub) var(--fs-xl) var(--serif); color: var(--color-heading); margin: 0 0 var(--space-2); }
.promesa p { color: var(--color-text-secondary); font-size: var(--fs-sm); line-height: 1.6; margin: 0; }
.promesas-pie { margin: var(--space-5) 0 0; text-align: center; font-size: var(--fs-sm); }
.promesas-pie a { color: var(--color-primary-ink); font-weight: var(--w-strong); }

/* ── Elegir por tarjeta ──
   Un <select> esconde sus opciones hasta que lo abres: una decisión de tres caminos acaba
   viéndose igual que un campo de texto vacío. Aquí las tres salen a la vista, cada una con
   sitio para una línea que la explique, y la elegida se rellena.
   El <select> sigue debajo, recortado pero enfocable: es el que lleva el name, el required
   y la validación. Si se ocultara con [hidden], el navegador se negaría a enfocarlo para
   avisar de un error y el envío fallaría en silencio. */
/* El segundo selector no es redundante: `.field select` fija width:100% con especificidad
   (0,2,0) y le gana a `.select--mirrored` (0,1,0), así que el control recortado seguía
   midiendo el ancho completo y empujaba la caja hacia la derecha. Se vio midiendo el
   desbordamiento, no mirando: como está recortado, no se ve nada raro. */
.select--mirrored,
.field select.select--mirrored { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; clip-path: inset(50%); overflow: hidden; }
.opt-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--space-2); }
.opt-cards[hidden] { display: none; }
.opt-card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  text-align: left;
  padding: var(--space-3) var(--space-4);
  min-height: 58px;
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--r);
  background: var(--color-surface-sunken);
  color: var(--color-text);
  font: var(--w-ui) var(--fs-sm)/1.35 var(--font);
  cursor: pointer;
  transition: background var(--ease), border-color var(--ease);
}
.opt-card:hover { background: var(--color-card); border-color: var(--color-primary); }
.opt-card:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.opt-card__txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.opt-card strong { font-weight: var(--w-strong); color: var(--color-heading); }
.opt-card small { font-size: var(--fs-2xs); color: var(--color-text-secondary); }
/* La elegida se rellena, que es lo que hace que se vea desde lejos cuál está puesta. El
   texto pasa entero a la tinta de contraste: dos tonos sobre un relleno saturado no se
   distinguen y sólo hacen ruido. */
.opt-card[aria-pressed="true"] { background: var(--color-primary); border-color: var(--color-primary); }
.opt-card[aria-pressed="true"] strong,
.opt-card[aria-pressed="true"] small { color: var(--color-on-accent); }
.opt-card[aria-pressed="true"]:hover { background: var(--color-primary-hover); border-color: var(--color-primary-hover); }

/* ── Superficies que responden ──
   REGLA: hundido = te responde · al nivel del panel = sólo informa.
   No es una convención nueva: es la de los campos, que ya iban hundidos, extendida a todo
   lo demás que se pulsa. Hacía falta porque medido no había ninguna diferencia — un <select>
   estaba 1.128 por encima de su contenedor y un panel estático 1.118. La diferencia entre
   "esto se abre" y "esto es una caja" era una centésima.
   Todo lo que va hundido lleva además --color-border-strong y un :hover que se ve. */
.support-vak { display: grid; gap: 8px; }
/* Cada vía es un <details> plegable: colapsado muestra una fila; abierto, sus chips. */
.vak-group { border: 1.5px solid var(--color-border-strong); border-radius: var(--r-sm); background: var(--color-surface-sunken); overflow: hidden; }
.vak-group[open] { border-color: var(--color-primary-mid); }
.vak-head { display: flex; align-items: center; gap: 10px; padding: 9px 12px; cursor: pointer; list-style: none; user-select: none; }
.vak-head::-webkit-details-marker { display: none; }
.vak-head:hover { background: var(--color-card); }
.vak-group:hover { border-color: var(--color-primary); }
.vak-badge { flex: none; width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; border-radius: 7px; background: var(--color-primary-soft); color: var(--color-primary); font: 600 15px/1 var(--serif); transition: background var(--ease), color var(--ease); }
.vak-group[open] .vak-badge { background: var(--color-primary); color: var(--color-on-accent); }
.vak-heading { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.vak-title { display: inline-flex; align-items: center; gap: 6px; font: 600 14px/1.2 var(--serif); color: var(--color-heading); }
.vak-title svg { flex: none; width: 14px; height: 14px; color: var(--color-primary); }
.vak-sub { font-size: 11.5px; font-weight: 500; color: var(--color-text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vak-group[open] .vak-sub { display: none; }
.vak-count { flex: none; min-width: 19px; height: 19px; padding: 0 6px; display: inline-flex; align-items: center; justify-content: center; border-radius: 999px; background: var(--color-primary); color: var(--color-on-accent); font: 600 11px/1 var(--font); }
.vak-count[hidden] { display: none; }
.vak-chevron { flex: none; width: 16px; height: 16px; color: var(--color-text-tertiary); transition: transform var(--ease); }
.vak-group[open] .vak-chevron { transform: rotate(180deg); }
/* Mismo caso que .ajustes-body, y por la misma razón: lo que se pulsa es la cabecera del
   grupo, no su cuerpo. El cuerpo sube al papel para que las etiquetas de dentro —que SÍ se
   pulsan— puedan hundirse contra él. El filete además separa la cabecera de la lista: son
   quince etiquetas seguidas, y sin línea la cabecera se lee como una más. */
.vak-body { padding: 12px; background: var(--color-card); border-top: 1px solid var(--color-border-strong); }
/* ── Los chips tenían un problema de significado, no de estilo ──
   Nacían con --color-primary-soft de fondo y --color-primary-mid de borde: EL MISMO tinte que
   este sistema usa para decir "activo" —la pestaña actual de la barra, la tarjeta de nivel
   elegida—. Así que las quince estrategias se veían medio encendidas de entrada, y la única
   diferencia entre elegida y no elegida era pasar de un verde claro a uno sólido. Con quince
   a la vez, saber qué habías marcado exigía compararlas entre sí.
   Ahora son tres estados que se distinguen sin comparar: sin elegir va HUNDIDA sobre el papel
   del cuerpo —que es lo que esta hoja usa para decir "esto te responde", igual que un campo—,
   el puntero encima trae el tinte, y la elegida va rellena de color.
   El salto de relleno es además una señal de PESO, no sólo de color, que es lo que 1.4.1
   pide cuando el estado no puede depender del tono. */
.chips { display: flex; flex-wrap: wrap; gap: 7px; }
.vak-options { margin-top: 2px; }
.chip { position: relative; display: inline-flex; align-items: center; border: 1px solid var(--color-border-strong); background: var(--color-surface-sunken); color: var(--color-text); border-radius: 999px; font: 500 12.5px/1.25 var(--font); cursor: pointer; transition: background var(--ease), border-color var(--ease), color var(--ease); }
.chip > span { padding: 5px 12px; }
.chip input { position: absolute; width: 1px; height: 1px; opacity: 0; margin: 0; pointer-events: none; }
/* En el dedo, 30px de alto: son objetivos contiguos y 2.5.8 pide 24 como mínimo absoluto. */
@media (pointer: coarse) { .chip > span { padding: 8px 13px; } }
.chip:hover { border-color: var(--color-primary); background: var(--color-primary-soft); }
.chip:focus-within { outline: 2px solid var(--color-primary); outline-offset: 1px; }
.chip.is-on { background: var(--color-primary); border-color: var(--color-primary-hover); color: var(--color-on-accent); }
/* Estrategias agregadas por el usuario: acento miel y botón para quitar. */
.chip.is-custom { background: var(--color-accent-soft); border-color: var(--color-accent); color: var(--color-heading); }
.chip.is-custom.is-on { background: var(--color-accent-soft); border-color: var(--color-accent); color: var(--color-heading); }
.chip.is-custom > span { padding-right: 4px; }
.chip-remove { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 17px; height: 17px; margin-right: 6px; border: none; border-radius: 50%; background: var(--color-danger-soft); color: var(--color-danger); font-size: 14px; line-height: 1; cursor: pointer; transition: background var(--ease); }
.chip-remove:hover { background: var(--color-danger-border); }
.vak-add { display: flex; gap: 8px; margin-top: 10px; }
.vak-add .vak-add-input { flex: 1; width: auto; min-width: 0; }
.vak-add-btn { flex: none; padding: 7px 14px; min-height: 0; font-size: 12.5px; }

/* ── Notices ── */
.notice {
  padding: 13px 16px;
  border-radius: var(--r-sm);
  background: var(--color-primary-soft);
  color: var(--color-text);
  font-size: 13px;
  line-height: 1.55;
  margin: 14px 0;
}
.notice[hidden] { display: none; }
/* Región de estado vacía: presente en el árbol de accesibilidad (aria-live) pero sin caja visual. */
.notice:empty { padding: 0; border: 0; margin: 0; background: none; }
/* Avisos con icono (SVG vía mask, tinta currentColor — sin glifos tipográficos). */
.notice-success, .notice-warning, .notice-danger { position: relative; padding-left: 44px; }
.notice-success::before, .notice-warning::before, .notice-danger::before {
  content: '';
  position: absolute;
  left: 15px;
  top: 14px;
  width: 17px;
  height: 17px;
  background: currentColor;
  -webkit-mask: var(--notice-icon) center / contain no-repeat;
  mask: var(--notice-icon) center / contain no-repeat;
}
.notice-success {
  --notice-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21.801 10A10 10 0 1 1 17 3.335'/%3E%3Cpath d='m9 11 3 3L22 4'/%3E%3C/svg%3E");
  background: var(--color-success-soft);
  border-left: 3px solid var(--color-primary);
  color: var(--color-success-ink);
}
.notice-warning {
  --notice-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3C/svg%3E");
  background: var(--color-warning-soft);
  border-left: 3px solid var(--color-warning);
  color: var(--color-warning-ink);
}
.notice-danger {
  --notice-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 8v4'/%3E%3Cpath d='M12 16h.01'/%3E%3C/svg%3E");
  background: var(--color-danger-soft);
  border-left: 3px solid var(--color-danger);
  color: var(--color-danger-ink);
}

/* ── Table rows (history, admin) ── */
.table-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
  transition: background var(--ease);
}
.table-row:last-child { border-bottom: none; }
/* Sin cebra. Franja alterna, línea inferior Y realce al pasar el puntero son TRES
   separadores discutiendo por la misma fila: de ahí venía buena parte de la sensación de
   enredo, y con dos líneas de texto por fila la franja no aportaba nada que la línea no
   dijera ya. Al quitarla hay que quitar TAMBIÉN su pareja del tema oscuro (estaba lejos,
   entre los ajustes de [data-theme="dark"]): dejar una sola vivas es peor que tener las dos. */
.table-row:hover { background: var(--color-primary-soft); box-shadow: inset 3px 0 0 var(--color-accent); }
.table-row > span:first-child { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
/* La columna de texto era antes un bloque con <br> entre el título y su detalle. Al pasarla
   a columna flex ese <br> se convierte en un elemento más y mete una línea en blanco. Se
   anula aquí, en un sitio, en vez de editar los trece listados que lo traen. */
.table-row > span:first-child br { display: none; }
/* `min-width: 0` en la columna del texto: sin él, un título largo estira la fila e
   empuja los botones fuera de la caja en vez de recortarse. */
.table-row strong { font-size: 14.5px; font-weight: 650; color: var(--color-heading); line-height: 1.35; }
/* El título como enlace: sin subrayado permanente (una lista de títulos subrayados se lee
   como un bloque de tinta), pero subrayado al pasar por encima y con foco visible. */
.table-row strong a { color: inherit; text-decoration: none; }
.table-row strong a:hover { text-decoration: underline; text-underline-offset: 2px; }
.table-row strong a:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; border-radius: 2px; }
.table-row small { font-size: 12px; color: var(--color-text-secondary); line-height: 1.5; }
.table-row span:last-child { display: flex; gap: 6px; flex-wrap: wrap; flex-shrink: 0; align-items: center; }
/* Los controles de una fila son secundarios frente a su contenido: se compactan para que
   la lista se lea como una lista y no como una rejilla de botones. */
.table-row span:last-child .outline { padding: 6px 11px; font-size: 12.5px; }

/* ── Menú «⋯» de una fila ── */
.row-menu { position: relative; display: inline-flex; }
.row-menu__btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0;
  border: 1.5px solid transparent; border-radius: var(--r-sm);
  background: none; color: var(--color-text-secondary); cursor: pointer;
  transition: background var(--ease), color var(--ease), border-color var(--ease);
}
.row-menu__btn:hover { background: var(--color-surface-sunken); color: var(--color-text); border-color: var(--color-border); }
.row-menu__btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 1px; }
.row-menu.is-open .row-menu__btn { background: var(--color-surface-sunken); color: var(--color-text); border-color: var(--color-border); }
.row-menu__pop {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 300;
  min-width: 168px; padding: 4px;
  display: flex; flex-direction: column;
  background: var(--color-surface); border: 1.5px solid var(--color-border);
  border-radius: var(--r-sm); box-shadow: var(--shadow-lg);
}
.row-menu__pop[hidden] { display: none; }
.row-menu__item {
  padding: 9px 11px; border: 0; border-radius: 4px;
  background: none; color: var(--color-text); cursor: pointer;
  font: 500 13px/1.35 var(--font); text-align: left; white-space: nowrap;
}
.row-menu__item:hover, .row-menu__item:focus-visible { background: var(--color-primary-soft); color: var(--color-primary); outline: none; }
/* Borrar desde el menú de fila. El hover se pintaba con el tinte del acento CÁLIDO, así que
   la acción de borrar se iluminaba en amarillo: dos señales distintas para lo mismo. */
.row-menu__item.is-danger { color: var(--color-danger); }
.row-menu__item.is-danger:hover, .row-menu__item.is-danger:focus-visible { background: var(--color-danger-soft); color: var(--color-danger-ink); }

/* Renombrar en el sitio: el título de la fila se convierte en este campo. */
.row-rename {
  width: 100%; max-width: 420px; padding: 4px 9px;
  border: 1.5px solid var(--color-primary); border-radius: var(--r-sm);
  background: var(--color-surface); color: var(--color-text);
  font: 650 14.5px/1.35 var(--font);
}
.row-rename:focus { outline: none; box-shadow: 0 0 0 3px rgb(var(--color-primary-rgb) / .12); }

/* ── Document viewer ── */
.document-toolbar {
  display: flex;
  gap: 16px;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  margin-bottom: 24px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--color-border);
}
/* Bloque de título (eyebrow + h1) apilado; solo la fila de acciones va en horizontal. */
.document-toolbar > div:first-child { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.document-toolbar > div:last-child { display: flex; gap: 8px; flex-wrap: wrap; }
.document-toolbar h2 {
  font: 540 22px var(--serif);
  color: var(--color-heading);
  margin: 0;
}
.document-section {
  padding: 20px 0;
  border-bottom: 1px solid var(--color-border);
}
.document-section:last-child { border-bottom: none; }
.document-section h3 {
  font: 600 13px/1 var(--font);
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--color-primary);
  margin: 0 0 10px;
}
.document-section [contenteditable] {
  line-height: 1.7;
  outline-color: var(--color-primary);
  min-height: 28px;
  font-size: 14px;
  color: var(--color-text);
}
/* Documento profesional: encabezado de identificación, listas, escala y pie. */
.doc-header { margin-bottom: 20px; }
.doc-meta { width: 100%; border-collapse: collapse; font-size: 13px; }
.doc-meta th { text-align: left; color: var(--color-primary); background: var(--color-primary-soft); font-weight: 600; padding: 7px 10px; border: 1px solid var(--color-border); width: 15%; vertical-align: top; }
.doc-meta td { padding: 7px 10px; border: 1px solid var(--color-border); vertical-align: top; }
.document-section ul { margin: 0; padding-left: 20px; }
.document-section li { margin-bottom: 6px; line-height: 1.6; }
table.escala { width: 100%; border-collapse: collapse; font-size: 13px; }
table.escala th, table.escala td { border: 1px solid var(--color-border); padding: 7px 10px; text-align: left; }
table.escala th { background: var(--color-primary-soft); color: var(--color-heading); font-weight: 600; }
table.escala td.mark { width: 44px; text-align: center; }
.doc-footer { margin-top: 24px; padding-top: 12px; border-top: 1px solid var(--color-border); font-size: 12px; color: var(--color-text-tertiary); }
/* Membrete profesional (Brand Kit): encabezado con logo/datos y firma al pie. */
.doc-letterhead { display: flex; align-items: center; gap: 16px; margin-bottom: 20px; padding-bottom: 14px; border-bottom: 2px solid var(--color-primary); }
.doc-logo { max-height: 64px; max-width: 180px; object-fit: contain; }
.doc-letterhead-text { display: flex; flex-direction: column; gap: 2px; }
.doc-brand-name { font: 600 16px var(--serif); color: var(--color-heading); }
.doc-brand-role { font-size: 13px; color: var(--color-primary); }
.doc-brand-inst { font-size: 13px; color: var(--color-text-tertiary); }
.doc-signature { margin-top: 40px; }
.doc-signature-line { width: 240px; border-top: 1px solid var(--color-heading); margin-bottom: 6px; }
.doc-signature-name { font-weight: 600; color: var(--color-heading); font-size: 14px; }
.doc-signature-role { font-size: 12px; color: var(--color-text-tertiary); }

/* ── Hero (landing page) ──
   Aquí vivía `.hero`, un hero de 58px anterior al rediseño del landing. Se borró: ninguna
   página lo usaba (el landing usa `.landing-hero`, más abajo) y arrastraba dos overrides
   responsive que también se fueron. `.lead` y `.hero-actions` sí siguen en uso. */
.lead { font-size: var(--fs-lg); color: var(--color-text-secondary); line-height: 1.65; }
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin: 22px 0;
}
.hero-actions a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  white-space: normal;
  text-align: center;
}

/* ── Pager ── */
.pager {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 20px;
  font-size: 13px;
  color: var(--color-text-secondary);
}

/* ── Barra de secciones ──
   Existía sólo como style="display:flex;gap:16px;…" repetido en las cinco páginas de admin.
   Ahora es un componente, porque cada sección de la app tiene una: es lo que sustituye al
   contenido de los antiguos paneles desplegables. La diferencia no es de estilo — el panel
   escondía hasta que preguntabas, la barra enseña dónde estás y qué hay al lado.
   Marca la página actual con subrayado verde, el mismo lenguaje que la barra de arriba,
   para que "estás aquí" se diga siempre igual. */
.subnav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  margin: 0 0 var(--space-6);
  border-bottom: 1px solid var(--color-border);
}
/* La barra pertenece al título, así que se le acerca. Sin :has() quedan los 34px normales
   del .page-title: más aire del deseado, nada roto. */
.page-title:has(+ .subnav) { margin-bottom: var(--space-4); }
.subnav a {
  display: inline-flex;
  align-items: center;
  /* 44px de alto: mismo objetivo táctil que el resto de la navegación. */
  min-height: 44px;
  padding: 0 var(--space-3);
  font-size: var(--fs-sm);
  font-weight: var(--w-ui);
  color: var(--color-text-secondary);
  border-bottom: 2px solid transparent;
  transition: color var(--ease), border-color var(--ease);
}
.subnav a:hover { color: var(--color-primary); border-bottom-color: var(--color-primary-mid); }
.subnav a[aria-current="page"] { color: var(--color-primary); font-weight: var(--w-strong); border-bottom-color: var(--color-primary); }
@media (max-width: 700px) {
  /* En móvil la barra puede no caber: se desplaza en horizontal en vez de partirse en dos
     filas, que empujarían el contenido hacia abajo en la pantalla que menos alto tiene. */
  .subnav { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .subnav::-webkit-scrollbar { display: none; }
  .subnav a { white-space: nowrap; }
}

/* ── Primeros pasos (app.html) ──
   Tres estados, y la diferencia entre ellos tiene que verse de un vistazo: si los tres
   invitan igual, ninguno dice por dónde empezar, que es lo único que hace falta saber la
   primera vez que entras. Hecho = verde y sin botón. Activo = borde verde y botón sólido.
   Pendiente = apagado, y su botón es un <span>, no un enlace que no lleva a ninguna parte. */
#onboarding .credit-row { margin-top: var(--space-4); }
#onboarding .credit-bar { margin-bottom: var(--space-5); }
.paso h3 { display: flex; align-items: center; gap: var(--space-2); }
.paso-check { flex: none; color: var(--color-primary); }
.paso.is-done { border-color: var(--color-primary-mid); background: var(--color-primary-soft); }
.paso.is-done .eyebrow { color: var(--color-primary); }
.paso.is-active { border: 2px solid var(--color-primary); }
.paso.is-locked { opacity: .55; }
.paso-bloqueado {
  /* Mismo tamaño y forma que el botón que ocupará cuando le toque, para que la tarjeta no
     cambie de alto al desbloquearse. */
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 40px; padding: 0 var(--space-4);
  border-radius: var(--r-sm);
  font: 600 14px/1 var(--font);
  color: var(--color-text-tertiary);
  border: 1.5px solid var(--color-border);
  background: var(--color-surface-sunken);
  cursor: not-allowed;
}

/* Cabecera de panel con acción a la derecha: el título dice qué hay, el botón qué se puede
   añadir. Sustituye a tener el formulario de creación permanentemente abierto encima. */
.panel-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-4); }
.panel-head h2 { margin: 0; }

/* ── Estado vacío ──
   Sustituye a veintitrés frases sueltas en gris. Una lista vacía con una línea de texto
   perdida se lee como si algo hubiera fallado al cargar; con un icono, un título y una
   salida se lee como lo que es: todavía no hay nada, y esto es lo que puedes hacer.
   El icono va suelto, sin círculo de color detrás: un vacío no es un aviso, y rodearlo de
   color lo convertiría en lo más llamativo de una pantalla que no tiene nada que mostrar. */
/* `grid-column` aquí no es un descuido: el estado vacío se inserta en el MISMO contenedor que
   llenarían las tarjetas, y varios de esos contenedores son rejillas (.curso-grid,
   .recurso-grid, .grid). Sin esto ocupaba una sola celda —268px en la de cursos—, así que
   "Todavía no tienes cursos" salía centrado dentro de esa columna y por tanto pegado a la
   izquierda del panel, como si algo se hubiera colocado mal. En un contenedor que no es
   rejilla la propiedad se ignora sola, así que una regla cubre los dos casos. */
.empty { text-align: center; padding: var(--space-8) var(--space-5); grid-column: 1 / -1; }
/* El icono va dentro de un disco de color, no suelto. Antes era una marca de 34px en
   --color-primary-mid: 1.83 sobre la tarjeta, tan pálida que el hueco se leía como si algo
   hubiera fallado al cargar en vez de como "aquí todavía no hay nada". Con el disco se lee
   como una pieza puesta a propósito — y es el mismo recurso que usan las tres promesas de
   la portada, así que el producto dice la misma cosa de la misma forma en los dos sitios. */
.empty__ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 68px;
  height: 68px;
  border-radius: 50%;
  background: var(--color-primary-soft);
  color: var(--color-primary-ink);
  margin: 0 auto var(--space-4);
}
.empty__ico svg { width: 30px; height: 30px; }
.empty h3 { font: var(--w-sub) var(--fs-lg)/1.3 var(--serif); color: var(--color-heading); margin: 0 0 var(--space-2); }
.empty p { margin: 0 auto; max-width: 46ch; font-size: var(--fs-sm); color: var(--color-text-secondary); line-height: 1.55; }
.empty .btn, .empty .outline { margin-top: var(--space-5); }

/* ── Diálogos ──
   <dialog> nativo: showModal() ya trae trampa de foco, inertizado del fondo y ::backdrop.
   Lleva la esquina doblada de las cards porque es una pieza con carácter, no un contenedor. */
.dialog {
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--r-lg) var(--r-lg) var(--r-lg) 4px;
  max-width: 560px;
  width: calc(100vw - 32px);
  /* Un modal es lo más elevado que hay en la pantalla: va en el plano del papel, no en el
     del panel que quedó detrás. */
  background: var(--color-card);
  color: var(--color-text);
  box-shadow: var(--shadow-lg);
}
.dialog::backdrop { background: rgba(21, 39, 28, .45); }
.dialog__head { padding: var(--space-6) var(--space-6) 0; }
.dialog__head h2 { font: var(--w-title) var(--fs-2xl)/1.2 var(--serif); color: var(--color-heading); margin: 0; }
.dialog__body { padding: var(--space-4) var(--space-6); max-height: 70vh; overflow: auto; }
.dialog__text { margin: 0; color: var(--color-text); line-height: 1.6; }
.dialog__foot {
  display: flex; gap: var(--space-3); justify-content: flex-end; flex-wrap: wrap;
  padding: var(--space-4) var(--space-6) var(--space-6);
}
/* Cancelar a la izquierda del confirmar: la salida se lee antes que la acción. */
.dialog__foot .outline { order: -1; }
/* «Hay más abajo». Ocupa la fila entera por encima de los botones (order:-2 lo pone antes que
   el Cancelar, que ya va en -1) y sólo existe cuando de verdad queda contenido sin ver. Va en
   el pie y no flotando sobre el cuerpo: ahí es donde mira quien está a punto de pulsar
   «Continuar», que es justo el momento en que hay que avisarle. */
.dialog__mas { display: none; }
.dialog.is-hay-mas .dialog__mas {
  order: -2; width: 100%; display: flex; align-items: center; justify-content: center;
  gap: var(--space-2); margin-bottom: var(--space-3);
  padding: var(--space-2) var(--space-4);
  border: 1px dashed var(--color-border); border-radius: 999px;
  background: rgb(var(--color-primary-rgb) / .06);
  color: var(--color-heading); font: 500 12.5px/1.25 var(--font);
  cursor: pointer;
}
.dialog.is-hay-mas .dialog__mas:hover,
.dialog.is-hay-mas .dialog__mas:focus-visible {
  background: rgb(var(--color-primary-rgb) / .12); border-style: solid;
}
/* El corte del contenido, insinuado dentro del cuerpo: sin esto el texto termina en seco y se
   lee como un final, no como un recorte. */
.dialog.is-hay-mas .dialog__body { box-shadow: inset 0 -12px 12px -12px rgb(var(--color-primary-rgb) / .35); }
@media (prefers-reduced-motion: no-preference) {
  .dialog.is-hay-mas .dialog__mas-flecha { animation: mas-abajo 1.6s ease-in-out infinite; }
  @keyframes mas-abajo { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(3px); } }
}
@media (prefers-reduced-motion: no-preference) {
  .dialog[open] { animation: dialog-in .16s ease-out; }
  @keyframes dialog-in { from { opacity: 0; transform: translateY(6px); } }
}

/* ── Small note ── */
.small-note { font-size: 12px; color: var(--color-text-secondary); line-height: 1.5; }

/* Historial de versiones, dentro de documento.html. Cada sección con su rótulo y el texto
   respetando los saltos de línea que traía. */
.version-section { margin: var(--space-3) 0; }
.version-section p { margin: var(--space-1) 0 0; white-space: pre-wrap; }

/* ── Ayuda del campo ──
   Toda la ayuda del producto fue primero texto permanente —entre el 44% y el 79% de las
   palabras de cada pantalla— y después un "?" que abría un <details>. Ninguno de los dos
   extremos era el problema real: lo era la CANTIDAD. Un párrafo de cuarenta palabras molesta
   esté abierto o cerrado, y esconderlo tras un signo no lo acorta, sólo lo hace invisible
   para quien no sabe que existe. Recortada la ayuda a una o dos frases por campo, plegarla no
   compra nada y cuesta un clic justo a quien no entiende el campo.

   Vuelve a estar a la vista, pero ahora en un renglón fino con filete: se distingue de la
   etiqueta y del control sin robarle peso a ninguno, y el aria-describedby que ya apuntaba
   aquí sigue siendo el mismo — con la diferencia de que ahora quien mira la pantalla y quien
   usa lector de pantalla reciben exactamente lo mismo. */
.hint__body {
  margin: 0 0 var(--space-1);
  padding-left: var(--space-3);
  border-left: 2px solid var(--color-primary-mid);
  font-size: var(--fs-xs);
  color: var(--color-text-secondary);
  line-height: 1.55;
}
/* Dentro de un fieldset la ayuda va bajo la leyenda, separada del marco. */
fieldset.field > .hint__body { margin: var(--space-1) 0 var(--space-2); }

/* ── Barra de acción del formulario ──
   Un formulario de diecisiete campos tiene un problema que ninguna caja de aviso resuelve:
   para enviarlo hay que llegar al fondo, y desde el fondo ya no se ve lo que se rellenó.
   La barra se queda pegada abajo, así que el botón y la pista de "qué falta" acompañan el
   recorrido en vez de esperar al final. El degradado evita el corte duro cuando el
   contenido pasa por debajo. */
.form-actions {
  position: sticky;
  bottom: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-top: var(--space-5);
  padding: var(--space-4) 0;
  border-top: 1px solid var(--color-border);
  background: linear-gradient(to bottom, transparent, var(--color-surface) 45%);
}
/* La pista vive al lado del botón: dice UNA cosa, la siguiente. En verde sólo cuando ya no
   falta nada — antes de eso es información, no un logro. */
.form-actions #quality { margin: 0; }
.form-actions #quality.is-ready { color: var(--color-primary); font-weight: var(--w-strong); }
/* La versión del catálogo es metadato: baja al pie de los ajustes, donde se consulta si
   alguien la busca, en vez de ocupar una línea sobre el botón en cada carga. */
.catalog-note { margin: var(--space-3) 0 0; }
/* Saldo de créditos: primera línea del panel, no la última. Saber con qué cuentas es algo
   que se decide ANTES de rellenar diecisiete campos, no después. */
/* El saldo de créditos: violeta y chispa en su estado normal. Los dos estados de abajo
   pisan el color a propósito —pintar el chip de violeta siempre borraría la única señal de
   que se están acabando—, y la chispa se queda, heredando el color del estado. */
.plan-saldo { margin: 0 0 var(--space-4); display: inline-flex; align-items: baseline; flex-wrap: wrap; gap: 2px 6px; color: var(--color-ai-ink); }
/* El enlace hereda el color del estado —violeta, ámbar o rojo—, que es lo que dice cuántos
   quedan. Subrayarlo sólo al pasar por encima evita añadir una raya a una línea que ya está
   cargada de señal.
   Y va con `main p` delante por una razón medible: la regla de arriba del todo —los enlaces
   dentro de texto corrido se subrayan, para no depender del color (1.4.1)— suma (0,2,3) y le
   ganaba a `.plan-saldo a` (0,1,1). O sea que esto NUNCA se aplicó: el saldo salía subrayado
   siempre, que es justo lo contrario de lo que dice el párrafo anterior. Con `main p` y el
   `:not(.btn)` se empata en (0,2,3) y se gana por orden de origen, que es el mismo mecanismo
   que la sección del menú y la del panel de avisos ya documentan.
   La excepción está justificada, no es un capricho: 1.4.1 pide distinguir un enlace DENTRO de
   un texto que no lo es, y aquí no hay tal texto — la línea entera es el saldo, con su chispa
   delante y su color de estado. */
.plan-saldo a,
main p.plan-saldo a:not(.btn) { color: inherit; text-decoration: none; }
.plan-saldo a:hover, .plan-saldo a:focus-visible,
main p.plan-saldo a:not(.btn):hover { text-decoration: underline; text-underline-offset: 3px; }
/* La coletilla no es accionable: ni hereda el color del estado ni pide un clic. Es la nota al
   pie del número que tiene al lado. */
.plan-saldo__nota { color: var(--color-text-secondary); font-weight: 400; }
.plan-saldo:empty { display: none; }
/* El estado va por clase, no por style.color: los colores inline eran literales fijos y en
   tema oscuro se quedaban clavados en el valor del tema claro. Sólo se tiñe cuando hay algo
   que decir — un saldo holgado no necesita color, sólo estar. */
/* Los dos sufijos son los que emite updateCredits() en product-ui.js, no nombres de token:
   el chip se pinta con `plan-saldo--${estado}` y estado vale ok, warn o danger. */
.plan-saldo--warn { color: var(--color-warning-strong); font-weight: var(--w-strong); }
.plan-saldo--danger { color: var(--color-danger); font-weight: var(--w-strong); }

/* ── El ritmo que se estaba resolviendo a mano ──
   Estas cuatro reglas sustituyen a nueve style="margin-top:16px|20px|22px|10px" repartidos
   por app.html y planificacion.html. Un margen escrito en el atributo no es un error en sí;
   el problema es que cada uno eligió su número (10, 16, 20, 22) y así dos bloques que hacen
   lo mismo no respiran igual. Ahora salen de la rejilla de 4px como todo lo demás.
   `.panel + .panel` (arriba) ya cubría panel-tras-panel; lo que faltaba era grid-tras-panel
   y grid-dentro-de-panel. El :not(:first-child) es para evaluacion.html, donde la grilla
   abre el panel y no debe separarse de nada. */
.grid + .panel { margin-top: var(--space-6); }
.panel > .grid:not(:first-child) { margin-top: var(--space-4); }
.ajustes-body > label + .field { margin-top: var(--space-3); }
/* Un bloque que se separa del anterior con una línea, no con otra caja: el de "ajustar
   documento" bajo la vista previa. Una caja dentro de una caja pesa el doble que una regla. */
.section-split { margin-top: var(--space-5); padding-top: var(--space-4); border-top: 1px solid var(--color-border); }
.section-split > .eyebrow { margin: 0 0 var(--space-2); }
/* Botón y estado en la misma línea, que es como se leen: acción y su respuesta. */
.inline-actions { display: flex; gap: var(--space-3); align-items: center; flex-wrap: wrap; margin-top: var(--space-2); }

/* ── Etiqueta informativa (no interactiva) ──
   `.chip` no servía aquí: lleva cursor de mano, estado y un input dentro, y esto sólo
   nombra una categoría. */
.tag {
  display: inline-flex; align-items: center; padding: 3px 9px;
  border-radius: 999px; border: 1px solid var(--color-border); background: var(--color-surface-sunken);
  font: 600 11px/1.4 var(--font); color: var(--color-text-secondary);
  letter-spacing: .02em;
}
.tag--pro { border-color: var(--color-accent); background: var(--color-accent-soft); color: var(--color-heading); }

/* ── Resource blocks (library) ── */
/* Una tarjeta de recurso es una ficha para escanear, no una pieza de portada: menos aire
   dentro y una columna limpia de etiqueta → título → resumen → acción. */
.resource { padding: 20px 22px; display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.resource__tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.resource h3 { font: 560 18px var(--serif); color: var(--color-heading); margin: 0 0 6px; }
.resource p  { color: var(--color-text-secondary); line-height: 1.55; font-size: 13px; margin: 0 0 14px; }
/* La acción se pega abajo para que, en una fila de tarjetas de distinto alto, todos los
   botones queden alineados en vez de flotar cada uno donde acabe su resumen. */
.resource > :last-child { margin-top: auto; }

/* ── Actions row ── */
.actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 18px; }

/* ── Guía de la herramienta ──
   Fue una tarjeta con foto, luego un desplegable de una línea. Las dos versiones tenían el
   mismo problema y sólo la segunda lo escondía bien: bajo el título de una herramienta que no
   se ha usado nunca aparecía un rótulo plegado —«Cómo revisar una planificación»— que se lee
   como una pestaña más de la barra que tiene tres centímetros más arriba. Y lo que hay dentro
   es justamente lo que hace falta ANTES de empezar; ponerlo tras un clic es cobrarle un peaje
   a la única persona que lo necesita.
   Ahora son tarjetas numeradas y siempre a la vista: el paso a paso se ve de un vistazo y no
   compite con la navegación, porque un número grande dentro de un cuadro no se parece a nada
   que se pueda pulsar. `auto-fit` con mínimo de 15rem para que dos o tres pasos llenen la
   fila y en móvil caigan a una columna sin media query. */
.guia-pasos {
  margin: 0; padding: 0; list-style: none; counter-reset: guia;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--space-3);
}
.guia-paso {
  position: relative;
  padding: var(--space-4) var(--space-4) var(--space-4) var(--space-8);
  border: 1px solid var(--color-border);
  border-radius: var(--r-sm);
  background: var(--color-surface);
  font-size: var(--fs-sm); color: var(--color-text-secondary); line-height: 1.55;
}
.guia-paso::before {
  counter-increment: guia; content: counter(guia);
  position: absolute; left: var(--space-3); top: var(--space-3);
  /* Mismo numeral serif que `.tool-steps` en las fichas públicas: es el mismo contenido —el
     paso a paso de la herramienta— visto desde dentro de la aplicación. */
  font: var(--w-strong) 22px/1 var(--serif); font-optical-sizing: auto; color: var(--color-accent-ink);
}
.guia-paso b { display: block; margin-bottom: var(--space-1); color: var(--color-heading); font-weight: var(--w-strong); }
.guia { margin-bottom: var(--space-6); }
.guia > h2 { margin: 0 0 var(--space-3); font-size: var(--fs-md); }
/* Cuando cada paso lleva controles dentro —el alta de la verificación en dos pasos tiene un
   QR, una clave y dos campos— las columnas los estrujan. Mismo componente, una sola columna. */
.guia-pasos--columna { grid-template-columns: 1fr; }

/* ── Verificación en dos pasos ── */
.mfa-lista { list-style: none; margin: 0 0 var(--space-4); padding: 0; display: grid; gap: var(--space-2); }
.mfa-lista li { display: flex; flex-direction: column; padding: var(--space-3); border: 1px solid var(--color-border); border-radius: var(--r-sm); }
.mfa-lista small { color: var(--color-text-secondary); font-size: var(--fs-xs); }
.mfa-qr { margin: var(--space-3) 0; }
.mfa-qr img { max-width: 200px; width: 100%; height: auto; background: #fff; padding: var(--space-2); border-radius: var(--r-sm); }
/* La clave se escribe a mano en el teléfono: monoespaciada, con espacio entre letras y
   seleccionable de una pasada, porque copiarla mal es el fallo más probable del alta. */
.mfa-secret {
  margin: var(--space-2) 0 0; padding: var(--space-3);
  background: var(--color-surface-sunken); border: 1px solid var(--color-border-strong); border-radius: var(--r-sm);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--fs-sm);
  letter-spacing: .12em; word-break: break-all; user-select: all;
}
.service-catalog { margin-top: var(--space-7); }
/* Grillas de servicios: auto-fit para que un grupo de 2 tarjetas también llene la fila
   (la grilla base es de 3 columnas fijas y dejaría un hueco). La doble clase gana a las
   sobrescrituras de `.grid` en los media queries. */
.grid.service-catalog { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
/* Servicios agrupados por etapa: un acento por grupo. La rotación por posición
   (`.grid > .card:nth-child(3n…)`) cuenta por contenedor, así que al separar en varias
   grillas cada grupo partiría en verde y terracota casi desaparecería. */
.service-group + .service-group { margin-top: var(--space-5); }
.service-group .service-catalog { margin-top: var(--space-4); }
.service-group[data-accent="green"] .service-catalog > .card { border-top: 2px solid var(--color-primary); }
.service-group[data-accent="honey"] .service-catalog > .card { border-top: 2px solid var(--color-accent); }
.service-group[data-accent="terracotta"] .service-catalog > .card { border-top: 2px solid var(--color-coral); }
.service-catalog .card {
  transition: transform var(--ease), box-shadow var(--ease);
}
.service-catalog .card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
}
.service-icon {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: none;
  background: var(--color-primary-soft);
  border-radius: var(--r-sm) var(--r-sm) var(--r-sm) 3px;
  color: var(--color-primary);
  margin-bottom: 12px;
}
.service-icon svg { width: 20px; height: 20px; display: block; }
a.card:hover .service-icon, .help-links a:hover .service-icon { background: var(--color-primary); color: var(--color-on-accent); }
.help-links .service-icon { margin-bottom: 0; }
.help-links {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  margin-top: 22px;
}
.help-links a {
  display: flex;
  gap: 12px;
  align-items: center;
  padding: 16px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--r);
  font-size: 14px;
  transition: border-color var(--ease), background var(--ease);
}
.help-links a:hover { border-color: var(--color-primary); background: var(--color-primary-soft); }

/* ── Subscription summary ── */
.sub-summary { display: flex; flex-direction: column; gap: 18px; }
.sub-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.sub-head h2 { font: 540 25px var(--serif); color: var(--color-heading); margin: 3px 0 0; }
.status-pill { display: inline-flex; align-items: center; gap: 7px; padding: 6px 14px; border-radius: 20px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.status-pill::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.status-ok     { background: var(--color-primary-soft); color: var(--color-primary-ink); }
/* .status-ok lo comparten el chip de créditos de la barra y el estado "Suscripción activa"
   de la ficha de plan. Sólo el primero gasta créditos, así que el violeta se acota a él; la
   suscripción se queda en turquesa, que es lo que significa el turquesa aquí. */
.nav-plan .status-ok { background: var(--color-ai-soft); color: var(--color-ai-ink); }
.status-warn   { background: var(--color-warning-soft); color: var(--color-warning-strong); }
.status-danger { background: var(--color-danger-soft); color: var(--color-danger); }
.credit-block { display: flex; flex-direction: column; gap: var(--space-2); }
.credit-row { display: flex; justify-content: space-between; align-items: baseline; font-size: var(--fs-sm); color: var(--color-text-secondary); }
.credit-row strong { color: var(--color-heading); font-size: var(--fs-md); font-variant-numeric: tabular-nums; }
/* Los créditos son la moneda de la IA, así que van en violeta como lo que se gasta con ellos. */
.credit-bar { height: 9px; border-radius: 6px; background: var(--color-ai-mid); overflow: hidden; }
.credit-bar span { display: block; height: 100%; background: var(--color-ai); border-radius: 6px; transition: width var(--ease); }
.sub-summary .outline { align-self: flex-start; }

/* ── Plan cards ── */
/* Tarjetas de plan de suscripcion.html. Comparten toda la presentación con .plan-tier
   (la misma que el landing) y sólo añaden el gancho de "tu plan actual"; el estado
   .current lo pinta suscripcion-page.js y su regla vive junto a .plan-tier.featured,
   más abajo, para poder ganarle. */
.plan-card { overflow: visible; }
.plan-card .eyebrow { margin-bottom: 2px; }
.plan-badge { position: absolute; top: -11px; left: 22px; background: var(--color-primary); color: var(--color-on-accent); font-size: 10px; font-weight: 700; letter-spacing: .06em; padding: 5px 13px; border-radius: 20px; text-transform: uppercase; box-shadow: var(--shadow-sm); }

/* ══════════════════════════════════
   Landing
   ══════════════════════════════════ */

/* Hero asimétrico (no centrado, columnas desiguales) */
/* Excepción 1 al ritmo de `.wrap`: el hero abre la página y no tiene nada encima de lo que
   separarse, así que arranca más arriba y respira más abajo, contra la trustbar. */
.landing-hero { display: grid; grid-template-columns: minmax(0, 1.12fr) minmax(0, .88fr); gap: 60px; align-items: center; padding-top: var(--space-8); padding-bottom: var(--space-10); }
/* 52px de tope y no 56: con el peso 480 de Fraunces, a 56px el titular se comía media
   pantalla en un portátil de 13" y empujaba el CTA bajo el pliegue. */
.hero-copy h1 { font: var(--w-display) var(--fs-display-lg)/1.07 var(--serif); color: var(--color-heading); margin: var(--space-4) 0 var(--space-5); text-wrap: balance; letter-spacing: -.01em; }
.hero-mark { position: relative; color: var(--color-primary); white-space: nowrap; }
/* Subrayado "a mano": trazo ondulado dibujado en SVG, imperfección deliberada. */
.hero-mark::after { content: ''; position: absolute; left: -2px; right: -2px; bottom: -.1em; height: .2em; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 9' preserveAspectRatio='none'%3E%3Cpath d='M2 6.5 Q 20 1.5 40 5 T 78 4.5 T 118 5.5' fill='none' stroke='%23F4C95D' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat center / 100% 100%; transform: rotate(-.5deg); }
.hero-copy .lead { max-width: 46ch; }
.hero-copy .hero-actions { margin: var(--space-6) 0 0; }
.hero-trust { margin-top: var(--space-4); font-size: var(--fs-sm); color: var(--color-text-secondary); line-height: 1.6; }

/* Documento de muestra: un artefacto real, no una card de features */
.doc-preview { position: relative; margin: 0; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--r-lg) var(--r-lg) var(--r-lg) 4px; padding: var(--space-6) var(--space-6); box-shadow: var(--shadow-lg); transform: rotate(1.1deg); transition: transform .35s ease; }
.doc-preview:hover { transform: rotate(.3deg); }
.doc-preview::before { content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit; background: var(--color-surface-sunken); border: 1px solid var(--color-border); transform: rotate(-3deg) translate(-10px, 12px); opacity: .75; }
/* Cinta adhesiva: el documento está "pegado" al muro, no flotando en un mockup. */
.doc-preview::after { content: ''; position: absolute; top: -13px; left: 50%; width: 92px; height: 26px; transform: translateX(-50%) rotate(-2deg); background: rgba(242,230,207,.88); border: 1px solid rgba(201,145,63,.28); border-radius: 2px; box-shadow: var(--shadow-xs); }
.doc-preview__bar { display: flex; gap: var(--space-2); margin-bottom: var(--space-4); }
.doc-preview__bar span { width: 9px; height: 9px; border-radius: 50%; background: var(--color-primary-mid); }
.doc-preview__bar span:first-child { background: var(--color-accent); }
.doc-preview__eyebrow { font: 700 var(--fs-3xs)/1 var(--font); letter-spacing: .13em; color: var(--color-primary); margin: 0 0 var(--space-2); }
.doc-preview h3, .doc-preview__title { font: var(--w-sub) var(--fs-xl) var(--serif); color: var(--color-heading); margin: 0 0 var(--space-4); }
.doc-preview__section { margin-bottom: var(--space-3); }
.doc-preview__section strong { display: block; font-size: var(--fs-3xs); letter-spacing: .07em; text-transform: uppercase; color: var(--color-text-secondary); margin-bottom: 3px; }
.doc-preview__section p { margin: 0; font-size: var(--fs-sm); line-height: 1.55; color: var(--color-text); }
.doc-preview__tags { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-4); }
.doc-preview__tags span { font-size: var(--fs-2xs); padding: var(--space-1) 11px; border-radius: 20px; background: var(--color-primary-soft); color: var(--color-primary); font-weight: 600; }

/* Pasos — numeral grande en Fraunces (voz editorial, no el "01·02·03" genérico) */
.steps-title { font: 530 clamp(24px, 3.4vw, 31px)/1.15 var(--serif); color: var(--color-heading); max-width: 22ch; margin: 0 0 var(--space-7); text-wrap: balance; }
.steps-band { display: grid; grid-template-columns: repeat(3, 1fr); gap: 30px; counter-reset: paso; }
.step { border-top: 2px solid var(--color-primary); padding-top: var(--space-3); }
/* El número iba en el acento cálido y daba 1.57 sobre blanco: un 38px es texto grande y
   necesita 3:1, así que ni con esa holgura llegaba. En turquesa da 5.47. */
.step::before { counter-increment: paso; content: counter(paso); display: block; font: 600 38px/1 var(--serif); font-optical-sizing: auto; color: var(--color-primary); margin: 0 0 var(--space-2); }
.step h3 { font: 600 var(--fs-lg) var(--font); color: var(--color-heading); margin: 0 0 var(--space-2); }
.step p { margin: 0; color: var(--color-text-secondary); font-size: var(--fs-sm); line-height: 1.6; }

/* Planes — 3 tarjetas, la del medio destacada (no idénticas) */
.plans-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; align-items: start; margin-top: 8px; }
.plan-tier { position: relative; display: flex; flex-direction: column; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--r-lg); padding: var(--space-6) var(--space-6); box-shadow: var(--shadow-sm); }
/* Plan recomendado: aro de 2px + fondo sage. El aro va como box-shadow y no como borde
   para no alterar la caja (las tres tarjetas conservan el mismo padding y alto de línea).
   Cuidado al tocar :hover más abajo: si redefine box-shadow sin repetir el aro, lo borra. */
.plan-tier.featured { border-color: transparent; background: var(--color-primary-soft); box-shadow: 0 0 0 2px var(--color-primary), var(--shadow); }
/* El degradado se limita a `.btn`: en suscripcion.html, markCurrentPlan() cambia el botón
   del plan contratado a `.outline` y lo deshabilita — ahí NO debe verse como CTA primario. */
.plan-tier.featured .tier-cta-btn.btn {
  background: linear-gradient(to top, var(--color-primary), var(--color-primary-hover));
  border-color: transparent;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .2), 0 8px 18px -8px rgb(var(--color-primary-rgb) / .55);
}
.plan-tier.featured .tier-cta-btn.btn:hover {
  background: linear-gradient(to top, var(--color-primary-hover), var(--color-primary-active));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .2), 0 10px 22px -8px rgb(var(--color-primary-rgb) / .6);
}
/* "Tu plan actual" (suscripcion.html). Va DESPUÉS de .plan-tier.featured a propósito:
   con la misma especificidad gana por orden, así que si el plan contratado es además el
   recomendado manda este aro. El banderín se oculta porque quedaría justo debajo del
   badge .plan-badge que inyecta markCurrentPlan(). */
.plan-card.current { border-color: transparent; box-shadow: 0 0 0 2px var(--color-primary), var(--shadow); }
.plan-card.current .tier-flag { display: none; }
.tier-flag { position: absolute; top: -12px; left: 20px; background: var(--color-accent); color: var(--color-on-warm); font-size: var(--fs-3xs); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; padding: var(--space-1) var(--space-3); border-radius: 3px; transform: rotate(-2deg); box-shadow: var(--shadow-xs); }
.tier-name { font: var(--w-sub) var(--fs-xl) var(--serif); color: var(--color-heading); margin: 0 0 var(--space-2); }
.tier-price { font: 540 33px var(--serif); color: var(--color-heading); margin: 0 0 var(--space-3); }
/* Cifras de ancho fijo: el contador de pricing-ui.js sube de 0 al precio y sin esto la
   tarjeta "tiembla" al cambiar de dígitos. */
.tier-price[data-count] { font-variant-numeric: tabular-nums; }
.tier-price small { font-size: var(--fs-sm); color: var(--color-text-secondary); font-weight: 400; }
/* Con un código aplicado, el precio de lista se TACHA y el nuevo se pinta debajo, en un nodo
   aparte. Nunca se reescribe el de lista: pricing-ui.js lo anima y al terminar restaura su
   texto original, así que un descuento escrito ahí duraría lo que la animación. */
.tier-price--tachado { text-decoration: line-through; font-size: 21px; color: var(--color-text-tertiary); margin-bottom: 0; }
.tier-price-descuento { color: var(--color-primary-deep); }

/* ── Código de descuento ── */
.cupon__fila { display: flex; gap: var(--space-3); align-items: flex-end; flex-wrap: wrap; }
.cupon__fila .field { flex: 1 1 12rem; margin: 0; }
.cupon__fila input { text-transform: uppercase; letter-spacing: .06em; }
.tier-note { color: var(--color-text-secondary); font-size: var(--fs-sm); line-height: 1.55; margin: 0 0 var(--space-2); }
.tier-details { margin: 4px 0 var(--space-4); }
.tier-details summary { list-style: none; cursor: pointer; user-select: none; display: inline-flex; align-items: center; gap: var(--space-2); font-weight: 600; color: var(--color-primary); font-size: var(--fs-sm); padding: 4px 0; }
.tier-details summary:hover { color: var(--color-primary-ink); text-decoration: underline; text-underline-offset: 3px; }
.tier-details summary::-webkit-details-marker { display: none; }
.tier-details summary::after {
  content: '';
  width: 13px;
  height: 13px;
  background: currentColor;
  -webkit-mask: var(--icon-caret) center / contain no-repeat;
  mask: var(--icon-caret) center / contain no-repeat;
  transition: transform var(--ease);
}
.tier-details[open] summary::after { transform: rotate(180deg); }
/* Encabezado de la lista ("Todo lo del plan Inicio, más:"): abre el bloque de beneficios
   y hace de separador entre la zona de decisión (precio + botón) y el detalle. */
.tier-includes { margin: var(--space-4) 0 0; padding-top: var(--space-4); border-top: 1px solid var(--color-border); font-size: var(--fs-sm); font-weight: 600; color: var(--color-heading); }
.plan-tier.featured .tier-includes { border-top-color: var(--color-primary-mid); }
.tier-features { list-style: none; margin: var(--space-3) 0 2px; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.tier-features li { position: relative; padding-left: 30px; font-size: var(--fs-sm); line-height: 1.5; color: var(--color-text); }
/* Check dentro de un círculo: ::before es el círculo, ::after el visto. */
.tier-features li::before { content: ''; position: absolute; left: 0; top: 1px; width: 20px; height: 20px; border: 1px solid var(--color-primary); border-radius: 50%; background: var(--color-surface); }
.tier-features li::after { content: ''; position: absolute; left: 7px; top: 5px; width: 5px; height: 9px; border: solid var(--color-primary); border-width: 0 2px 2px 0; transform: rotate(45deg); }
.tier-cta-btn { width: 100%; margin-top: var(--space-3); }

/* Cierre — banda editorial en tinta verde con la estrella como marca de agua */
.closing-card {
  position: relative;
  overflow: hidden;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 28px 56px;
  /* El tono profundo y no el base, y esto no es estético: sobre --color-primary el pie de la
     banda (#bcd3c2, 12.5px) daba 3.45 y el párrafo (#d9e8dd) 4.31 — los dos por debajo de AA,
     y el primero ya incumplía antes de esta paleta, con el verde. Sobre el tono profundo
     suben a 4.73 y 5.92. No hay ningún tinte que cumpla sobre el base: el mejor candidato se
     queda en 4.46. */
  background: var(--color-primary-deep);
  border-radius: var(--r-lg) var(--r-lg) var(--r-lg) 4px;
  padding: 48px 60px;
  color: #fff;
  box-shadow: var(--shadow-lg);
}
.closing-msg { min-width: 0; }
.closing-action { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); }
.closing-card::after {
  content: '';
  position: absolute;
  right: -40px;
  top: -46px;
  width: 230px;
  height: 230px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M16 5 C16.6 11 21 15.4 27 16 C21 16.6 16.6 21 16 27 C15.4 21 11 16.6 5 16 C11 15.4 15.4 11 16 5 Z' fill='%23F4C95D'/%3E%3C/svg%3E") no-repeat center / contain;
  opacity: .22;
  transform: rotate(14deg);
  pointer-events: none;
}
.closing-card .eyebrow { color: var(--color-accent-soft); }
.closing-card h2 { font: 520 clamp(26px, 3.2vw, 40px)/1.1 var(--serif); font-optical-sizing: auto; color: #fff; margin: var(--space-2) 0 var(--space-3); max-width: 22ch; text-wrap: balance; }
.closing-msg p { color: #DDE6D6; max-width: 60ch; font-size: var(--fs-lg); line-height: 1.6; margin: 0; }
.closing-card .btn { background: #fff; color: var(--color-primary-deep); border-color: #fff; box-shadow: none; white-space: nowrap; padding-left: var(--space-6); padding-right: var(--space-6); }
.closing-card .btn:hover { background: var(--color-accent-soft); border-color: var(--color-accent-soft); color: var(--color-heading); }
.closing-card .closing-fine { color: #C3CFB8; font-size: var(--fs-xs); margin: 0; }

/* ── Demo animado del hero (doc-preview): "míralo generarse" ──
   Degrada a estático sin JS (todo visible por defecto); JS agrega .is-generating. */
.doc-preview__gen { margin-left: auto; font-style: normal; font: 600 var(--fs-3xs)/1 var(--font); letter-spacing: .06em; text-transform: uppercase; color: var(--color-accent-ink); display: inline-flex; align-items: center; gap: var(--space-2); }
.doc-preview__gen::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; animation: gen-pulse 1s ease-in-out infinite; }
.doc-preview__gen.is-done { color: var(--color-primary); }
.doc-preview__gen.is-done::before { animation: none; }
@keyframes gen-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
[data-demo-line] { transition: opacity .5s ease, transform .5s ease; }
.doc-preview.is-generating [data-demo-line] { opacity: 0; transform: translateY(9px); }
.doc-preview.is-generating [data-demo-line].is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  [data-demo-line] { transition: none; }
  .doc-preview__gen::before { animation: none; }
}

/* ── Vida y movimiento del landing ──
   Entradas al cargar, revelado al hacer scroll y micro-interacciones.
   El estado oculto de [data-reveal] solo aplica bajo html.js-reveal (lo agrega el JS
   cuando hay IntersectionObserver y no hay reduce-motion): sin JS la página queda completa. */
@keyframes rise-in { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
.js-reveal [data-reveal] { opacity: 0; }
/* Estado final visible en la regla (no en la animación): si la animación no corre, igual se ve.
   El escalonado lo aplica el JS retrasando la clase, no animation-delay. */
.js-reveal [data-reveal].is-revealed { opacity: 1; animation: rise-in .65s cubic-bezier(.2, .7, .3, 1); }
/* ── Titular "en corte vertical": cada palabra sube desde detrás de su propia máscara ──
   El envoltorio lo crea pricing-ui.js. Sin JS el <h2> queda tal cual, y el estado final
   (translate:none) vive en la regla base: si la animación no corre, el texto igual se lee. */
/* El padding/margen negativo da aire bajo la línea base: sin él, overflow:hidden
   recortaría las colas de la "j", la "g" y la "p" del titular. */
.cut-w { display: inline-flex; overflow: hidden; vertical-align: bottom; padding-bottom: .14em; margin-bottom: -.14em; }
.cut-w > span { display: inline-block; will-change: transform; }
@media (prefers-reduced-motion: no-preference) {
  [data-cut-reveal].is-cut .cut-w > span { animation: cut-up .75s cubic-bezier(.16, 1, .3, 1) backwards; animation-delay: calc(var(--i) * 65ms); }
}
@keyframes cut-up { from { transform: translateY(105%); } }
/* Entrada de las tarjetas en suscripcion.html. Ahí no corre index-page.js (el revelado
   por scroll es exclusivo del landing), así que el escalonado se resuelve en CSS puro. */
@media (prefers-reduced-motion: no-preference) {
  #plans .plan-tier { animation: rise-in .6s cubic-bezier(.2, .7, .3, 1) backwards; }
  #plans .plan-tier:nth-child(2) { animation-delay: .09s; }
  #plans .plan-tier:nth-child(3) { animation-delay: .18s; }
}
@media (prefers-reduced-motion: no-preference) {
  /* Entrada del hero: el texto sube escalonado y el subrayado "se dibuja". */
  .hero-copy > * { animation: rise-in .6s cubic-bezier(.2, .7, .3, 1) backwards; }
  .hero-copy > :nth-child(1) { animation-delay: .05s; }
  .hero-copy > :nth-child(2) { animation-delay: .14s; }
  .hero-copy > :nth-child(3) { animation-delay: .23s; }
  .hero-copy > :nth-child(4) { animation-delay: .32s; }
  .hero-copy > :nth-child(5) { animation-delay: .41s; }
  .hero-mark::after { animation: underline-in .55s ease-out .95s backwards; }
  /* La estrella del cierre respira lentamente. */
  .closing-card::after { animation: star-drift 9s ease-in-out infinite alternate; }
}
@media (prefers-reduced-motion: no-preference) and (min-width: 861px) {
  /* La tarjeta demo entra asentándose y su hoja trasera deriva apenas (vida sin marear). */
  .doc-preview { animation: card-in .8s cubic-bezier(.2, .7, .3, 1) .3s backwards; }
  .doc-preview::before { animation: sheet-drift 7s ease-in-out 1.4s infinite alternate; }
}
@keyframes underline-in { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes card-in { from { opacity: 0; transform: rotate(3deg) translateY(30px); } to { opacity: 1; transform: rotate(1.1deg) translateY(0); } }
@keyframes sheet-drift { from { transform: rotate(-3deg) translate(-10px, 12px); } to { transform: rotate(-4.4deg) translate(-14px, 17px); } }
@keyframes star-drift { from { transform: rotate(8deg) scale(1); } to { transform: rotate(21deg) scale(1.06); } }
/* Micro-interacciones (respuesta al hover, breve y discreta). */
.trust-card { transition: transform .35s var(--ease), box-shadow .35s var(--ease), border-color .35s var(--ease); }
.trust-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); border-color: var(--color-primary-mid); }
.trust-ic { transition: transform .35s var(--ease); }
.trust-card:hover .trust-ic { transform: rotate(-8deg) scale(1.12); }
.plan-tier { transition: transform .35s var(--ease), box-shadow .35s var(--ease); }
.plan-tier:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
/* Repite el aro de 2px: sin él, pasar el puntero apagaría el borde del plan recomendado. */
.plan-tier.featured:hover { transform: translateY(-5px); box-shadow: 0 0 0 2px var(--color-primary), var(--shadow-lg); }
.step::before { transition: transform .3s var(--ease), color .3s var(--ease); }
.step:hover::before { transform: translateY(-3px) rotate(-4deg); color: var(--color-coral-ink); }

/* ── Banda de confianza (garantías reales, sin cifras inventadas) ── */
.trust-title { font: 530 clamp(23px, 3.2vw, 30px)/1.15 var(--serif); color: var(--color-heading); max-width: 24ch; margin: 0 0 30px; text-wrap: balance; }
.trust-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(218px, 1fr)); gap: var(--space-4); }
.trust-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--r-lg) var(--r-lg) var(--r-lg) 4px; padding: var(--space-5) var(--space-5) var(--space-6); box-shadow: var(--shadow-xs); }
.trust-ic { color: var(--color-primary); margin-bottom: var(--space-3); display: block; }
/* Los iconos van en las tintas y no en los tonos base: acompañan a un texto, así que se
   leen como parte de él y con el relleno claro casi desaparecerían sobre la tarjeta. */
.trust-card:nth-child(2) .trust-ic { color: var(--color-accent-ink); }
.trust-card:nth-child(4) .trust-ic { color: var(--color-coral-ink); }
.trust-card h3 { font: var(--w-sub) var(--fs-lg) var(--serif); color: var(--color-heading); margin: 0 0 var(--space-2); }
.trust-card p { margin: 0; font-size: var(--fs-sm); line-height: 1.55; color: var(--color-text-secondary); }
.trust-note { margin: var(--space-6) auto 0; max-width: var(--measure); text-align: center; font-size: var(--fs-sm); line-height: 1.6; color: var(--color-text); }

/* Pricing: microcopia bajo el CTA de cada plan */
.tier-foot { margin: var(--space-2) 0 0; text-align: center; font-size: var(--fs-xs); color: var(--color-text-tertiary); }

/* Navegación por anclas: aire bajo la barra fija de 72px. */
/* El header sticky (72px, más alto al envolver en móvil) taparía el destino de
   cualquier salto por ancla, incluido el skip-link que apunta a <main>. */
section[id], main[id] { scroll-margin-top: 92px; }

/* ── Franja de confianza bajo el hero (hechos verificables, sin cifras infladas) ── */
/* Excepción 2: la trustbar está SOLDADA al hero por diseño —es una franja con filete arriba
   y abajo, no una sección independiente—, así que no lleva separación superior. */
.trustbar { padding-top: 0; padding-bottom: var(--space-2); }
/* A 1300px los cinco hechos se reparten por toda la pantalla y la franja se lee como una
   línea dispersa en vez de como un grupo. */
.trustbar ul { list-style: none; margin: 0 auto; padding: var(--space-3) 0; max-width: 1100px; display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); border-top: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border); }
.trustbar li { font-size: var(--fs-xs); font-weight: 600; letter-spacing: .02em; color: var(--color-text-secondary); display: inline-flex; align-items: center; gap: var(--space-2); }
.trustbar li::before { content: ''; flex: none; width: 10px; height: 10px; background: var(--color-accent); -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M16 5 C16.6 11 21 15.4 27 16 C21 16.6 16.6 21 16 27 C15.4 21 11 16.6 5 16 C11 15.4 15.4 11 16 5 Z'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath d='M16 5 C16.6 11 21 15.4 27 16 C21 16.6 16.6 21 16 27 C15.4 21 11 16.6 5 16 C11 15.4 15.4 11 16 5 Z'/%3E%3C/svg%3E") center / contain no-repeat; }

/* ── Antes / Con Planifácil ── */
.compare h2 { font: 530 clamp(24px, 3.4vw, 31px)/1.15 var(--serif); color: var(--color-heading); max-width: 26ch; margin: 0 0 var(--space-6); text-wrap: balance; }
.compare-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-5); }
.compare-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--r-lg) var(--r-lg) var(--r-lg) 4px; padding: var(--space-6) var(--space-6); box-shadow: var(--shadow-xs); }
.compare-card h3 { font: var(--w-sub) var(--fs-lg) var(--serif); color: var(--color-heading); margin: 0 0 var(--space-3); }
.compare-card ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.compare-card li { position: relative; padding-left: var(--space-6); font-size: var(--fs-sm); line-height: 1.55; color: var(--color-text); }
.compare-before { background: var(--color-surface-sunken); }
.compare-before li::before { content: '—'; position: absolute; left: 2px; color: var(--color-text-tertiary); }
.compare-after { border-color: var(--color-primary-mid); }
.compare-after li::before { content: ''; position: absolute; left: 4px; top: 4px; width: 6px; height: 10px; border: solid var(--color-primary); border-width: 0 2px 2px 0; transform: rotate(45deg); }
.compare-note { margin: var(--space-4) 0 0; text-align: center; font-size: var(--fs-sm); color: var(--color-text-secondary); }

/* ── Herramientas: cada una con su costo en créditos y su plan reales ── */
.tool-group { margin-top: var(--space-6); }
.tools-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 12px; }
.tool-card { display: flex; flex-direction: column; gap: var(--space-2); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--r-lg) var(--r-lg) var(--r-lg) 4px; padding: var(--space-5); box-shadow: var(--shadow-xs); color: inherit; text-decoration: none; transition: transform .35s var(--ease), box-shadow .35s var(--ease), border-color .35s var(--ease); }
.tool-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); border-color: var(--color-primary-mid); }
.tool-card h3 { font: var(--w-sub) var(--fs-lg) var(--serif); color: var(--color-heading); margin: 0; display: flex; align-items: center; gap: var(--space-2); }
/* La clase va en el propio <svg>: se resetean el tamaño de caja y el margen que
   .service-icon (38px, pensado para un <span> contenedor) le impondría. */
.tool-card h3 .service-icon { flex: none; color: var(--color-primary); width: 20px; height: 20px; margin-bottom: 0; display: inline-block; }
.tool-card p { margin: 0; font-size: var(--fs-sm); line-height: 1.55; color: var(--color-text-secondary); flex: 1; }
.tool-badge { align-self: flex-start; font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .04em; padding: 4px var(--space-2); border-radius: 20px; background: var(--color-primary-soft); color: var(--color-primary); }
.tool-badge.pro { background: var(--color-accent-soft); color: var(--color-accent-ink); }
/* En las cards de servicios (display block) la píldora va en su propia línea. */
.card .tool-badge { display: table; margin: var(--space-2) 0; }

/* ── Mini-vistas decorativas de los pasos (aria-hidden) ── */
.step-visual { margin-top: var(--space-3); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--r); padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); box-shadow: var(--shadow-xs); font-size: var(--fs-3xs); color: var(--color-text-tertiary); }
.sv-field { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 26px; padding: 3px 9px; border: 1px solid var(--color-border); border-radius: 6px; background: var(--color-background); }
.sv-field b { font-weight: 400; color: var(--color-text-tertiary); }
.sv-chips { display: flex; gap: 5px; flex-wrap: wrap; }
.sv-chips span { padding: 3px 8px; border-radius: 20px; background: var(--color-primary-soft); color: var(--color-primary); font-weight: 600; }
.sv-line { height: 7px; border-radius: 4px; background: var(--color-surface-sunken); }
.sv-title { height: 9px; width: 55%; border-radius: 4px; background: var(--color-primary-mid); }
.sv-pills { display: flex; gap: 6px; flex-wrap: wrap; }
.sv-pills span { padding: 4px 10px; border-radius: 6px; border: 1px solid var(--color-border); background: var(--color-background); color: var(--color-text-secondary); font-weight: 600; }
.sv-pills span:first-child { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-on-accent); }

/* ── Ejemplo con el formato real del documento ── */
.example-doc { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--r-lg) var(--r-lg) var(--r-lg) 4px; box-shadow: var(--shadow); padding: 30px clamp(20px, 4vw, 44px) 26px; max-width: 860px; margin: 0 auto; }
.example-doc h3 { font: var(--w-sub) var(--fs-2xl) var(--serif); color: var(--color-heading); margin: 4px 0 var(--space-3); }
.example-meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--space-2) var(--space-4); margin: 0 0 var(--space-2); padding: var(--space-3) var(--space-3); background: var(--color-surface-sunken); border-radius: var(--r); font-size: var(--fs-xs); }
.example-meta > div { display: flex; flex-direction: column; gap: 1px; }
.example-meta dt { font-weight: 700; font-size: var(--fs-3xs); letter-spacing: .08em; text-transform: uppercase; color: var(--color-text-secondary); }
.example-meta dd { margin: 0; color: var(--color-text); }
.example-section { padding: var(--space-3) 0; border-top: 1px solid var(--color-border); }
.example-section h4 { font: 700 var(--fs-2xs)/1.3 var(--font); letter-spacing: .1em; text-transform: uppercase; color: var(--color-primary); margin: 0 0 var(--space-2); }
.example-section p { margin: 0; font-size: var(--fs-sm); line-height: 1.62; color: var(--color-text); }
.example-section ul { margin: 0; padding-left: var(--space-4); font-size: var(--fs-sm); line-height: 1.62; color: var(--color-text); }
.example-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); margin-top: 4px; }
.example-table th, .example-table td { border: 1px solid var(--color-border); padding: var(--space-2) var(--space-2); text-align: left; }
.example-table th { background: var(--color-surface-sunken); font-size: var(--fs-2xs); letter-spacing: .05em; text-transform: uppercase; color: var(--color-text-secondary); }
.example-table td:not(:first-child), .example-table th:not(:first-child) { width: 44px; text-align: center; }
.example-legend { margin: var(--space-2) 0 0; font-size: var(--fs-xs); color: var(--color-text-secondary); }
.example-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.example-scroll:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; border-radius: var(--r-sm); }
/* El resto del documento, plegado. Un ejemplo está para MIRARSE; leerlo entero era la
   condición que la página ponía antes de dejarte decidir. */
.example-expand { margin: var(--space-4) 0; border-top: 1px solid var(--color-border); }
.example-expand > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) 2px; min-height: 44px;
  font: var(--w-strong) var(--fs-sm)/1.35 var(--font); color: var(--color-primary);
}
.example-expand > summary::-webkit-details-marker { display: none; }
.example-expand > summary::after {
  content: ''; width: 7px; height: 7px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg); transition: transform var(--ease);
}
.example-expand[open] > summary::after { transform: translateY(1px) rotate(-135deg); }
.example-expand > summary:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; border-radius: var(--r-sm); }

.example-more { display: flex; flex-wrap: wrap; gap: var(--space-2); padding-top: var(--space-3); border-top: 1px solid var(--color-border); }
.example-more b { flex-basis: 100%; font: 700 var(--fs-2xs)/1.3 var(--font); letter-spacing: .1em; text-transform: uppercase; color: var(--color-text-secondary); margin: 0 0 2px; }
/* Neutros, no verdes. Eran catorce píldoras verdes que competían con los .tool-badge (que
   sí anuncian un precio) y con .doc-preview__tags en la misma página: el mismo verde
   significando seis cosas distintas deja de significar ninguna. Aquí sólo enumeran
   secciones, así que van en el tono de la interfaz. */
.example-more span { font-size: var(--fs-2xs); padding: 4px var(--space-2); border-radius: 20px; background: none; border: 1px solid var(--color-border); color: var(--color-text-secondary); font-weight: var(--w-ui); }
.example-foot { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-5); margin: var(--space-4) 0 0; font-size: var(--fs-xs); color: var(--color-text-secondary); }
.example-foot span { display: inline-flex; align-items: center; gap: var(--space-2); }
.example-foot span::before { content: ''; flex: none; width: 6px; height: 10px; border: solid var(--color-primary); border-width: 0 2px 2px 0; transform: rotate(45deg); }
.ejemplo-cta { text-align: center; margin-top: var(--space-6); }
.ejemplo-note { margin: var(--space-2) auto 0; max-width: var(--measure-narrow); font-size: var(--fs-xs); color: var(--color-text-secondary); line-height: 1.55; }

/* ── Fichas de herramienta (las 7 páginas públicas por herramienta) ── */

/* El costo iba incrustado a mitad del .lead de 17px en gris: el dato que decide la visita
   —cuánto cuesta y en qué plan está— enterrado dentro de una oración. Ahora es una línea
   propia entre el título y el resumen. */
.tool-meta { margin: var(--space-2) 0 var(--space-3); }

/* Los tres pasos de "Cómo funciona" eran un <ol> plano, mientras que ESOS MISMOS pasos en
   la portada (.steps-band) llevan numeral grande en Fraunces y filete verde. El sitio ya
   tenía un patrón para este contenido exacto y las fichas no lo usaban. Se reutiliza a
   menor escala: numeral de 24px, una columna, sin la mini-vista. */
.tool-steps { list-style: none; margin: 0; padding: 0; counter-reset: paso; display: flex; flex-direction: column; gap: var(--space-4); }
.tool-steps > li { position: relative; border-top: 2px solid var(--color-primary); padding: var(--space-3) 0 0 var(--space-8); }
.tool-steps > li::before {
  counter-increment: paso; content: counter(paso);
  position: absolute; left: 0; top: var(--space-2);
  /* Mismo caso que .step::before, arreglado al cambiar el tema claro: un número de 24px en
     negrita es texto grande y pide 3:1, y el amarillo se queda en 1.57. */
  font: var(--w-strong) 24px/1 var(--serif); font-optical-sizing: auto; color: var(--color-accent-ink);
}

/* Sustituye a seis style="text-align:center;color:var(--color-text-secondary);font-size:13.5px" idénticos,
   y de paso asciende "También te puede servir" de frase corrida a rótulo de sección. */
.tool-related { margin: var(--space-7) auto 0; max-width: var(--measure); text-align: center; font-size: var(--fs-xs); line-height: 1.6; color: var(--color-text-secondary); }
.tool-related b { display: block; margin-bottom: var(--space-2); font: var(--w-label) var(--fs-3xs)/1 var(--font); letter-spacing: .14em; text-transform: uppercase; color: var(--color-text-tertiary); }
.tool-related a { color: var(--color-primary); }

/* ── Casos de uso (sin testimonios inventados) ── */
.casos-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); }
/* Los tres perfiles viven ahora dentro de la sección "¿es para mí?", debajo del antes/después
   y de su nota: son la misma pregunta respondida por rol. */
.compare .casos-grid { margin-top: var(--space-7); }
.caso-card { background: var(--color-surface); border: 1px solid var(--color-border); border-left: 3px solid var(--color-primary); border-radius: var(--r) var(--r-lg) var(--r-lg) var(--r); padding: var(--space-5); box-shadow: var(--shadow-xs); }
.caso-card:nth-child(2) { border-left-color: var(--color-accent); }
.caso-card:nth-child(3) { border-left-color: var(--color-coral); }
.caso-card h3 { font: var(--w-sub) var(--fs-lg) var(--serif); color: var(--color-heading); margin: 0 0 var(--space-2); }
.caso-card p { margin: 0; font-size: var(--fs-sm); line-height: 1.6; color: var(--color-text-secondary); }

/* ── Explicación de créditos junto a los planes ── */
.credit-explainer { margin: var(--space-2) 0 var(--space-6); background: var(--color-surface); border: 1px dashed var(--color-ai-mid); border-radius: var(--r-lg); padding: var(--space-4) var(--space-5); display: grid; grid-template-columns: minmax(220px, .9fr) 1.1fr; gap: var(--space-3) 30px; align-items: center; }
.credit-explainer h3 { font: var(--w-sub) var(--fs-lg) var(--serif); color: var(--color-heading); margin: 0 0 var(--space-2); }
.credit-explainer > div p { margin: 0; font-size: var(--fs-sm); color: var(--color-text-secondary); line-height: 1.55; }
.credit-table { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.credit-table li { display: flex; justify-content: space-between; gap: var(--space-3); font-size: var(--fs-sm); color: var(--color-text); border-bottom: 1px dotted var(--color-border); padding-bottom: var(--space-2); }
.credit-table li:last-child { border-bottom: 0; padding-bottom: 0; }
.credit-table b { color: var(--color-ai-ink); white-space: nowrap; }

/* ── FAQ del landing: acordeón nativo details/summary (funciona sin JS) ── */
.faq-list { max-width: 760px; margin: 0 auto; border-top: 1px solid var(--color-border); }
/* La portada muestra cinco preguntas; las doce están en faq.html. Este enlace es lo que
   permite que la portada no tenga que repetir la FAQ entera. */
.faq-more { max-width: 760px; margin: var(--space-5) auto 0; text-align: center; font-size: var(--fs-sm); }
/* Objetivo táctil de 24px de alto (WCAG 2.2, 2.5.8 Target Size en nivel AA). El enlace es
   el único contenido de su párrafo, así que NO le vale la excepción de "enlace dentro de una
   frase": tiene que medir por sí mismo. Medía 18px. */
.faq-more a { display: inline-flex; align-items: center; min-height: 24px; padding: var(--space-1) var(--space-2); color: var(--color-primary); font-weight: var(--w-strong); text-decoration: none; }
.faq-more a:hover { text-decoration: underline; text-underline-offset: 2px; }
.faq-more a:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; border-radius: var(--r-sm); }
.faq-item { border-bottom: 1px solid var(--color-border); }
.faq-item summary { list-style: none; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: var(--space-4); padding: var(--space-4) 2px; font: 600 var(--fs-md)/1.4 var(--font); color: var(--color-heading); min-height: 44px; }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: ''; flex: none; width: 14px; height: 14px; background: var(--color-primary); -webkit-mask: var(--icon-caret) center / contain no-repeat; mask: var(--icon-caret) center / contain no-repeat; transition: transform var(--ease); }
.faq-item[open] summary::after { transform: rotate(180deg); }
.faq-item summary:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; border-radius: 4px; }
.faq-item div { padding: 0 var(--space-6) var(--space-4) 2px; font-size: var(--fs-sm); line-height: 1.65; color: var(--color-text-secondary); max-width: var(--measure); }
@media (prefers-reduced-motion: reduce) { .faq-item summary::after { transition: none; } }

/* ── Selector de nivel del demo del hero ── */
.hero-demo { min-width: 0; }
.demo-picker { margin: 16px 0 0; display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; justify-content: center; }
.demo-picker fieldset { border: 0; margin: 0; padding: 0; display: flex; gap: 8px; flex-wrap: wrap; }
.demo-chip { position: relative; margin: 0; }
.demo-chip input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
/* Conmutador de texto, NO píldora rellena. Estos tres controles eligen el nivel del ejemplo
   animado; con fondo y borde parecían tres botones más y competían con el CTA justo encima,
   que era la única acción real de la primera pantalla. Ahora en el hero nada tiene aspecto de
   botón salvo el CTA.
   El estado activo lo llevan DOS señales, color y subrayado: el subrayado solo no puede
   portarlo (WCAG 1.4.1, no depender únicamente del color... ni únicamente de un adorno). */
.demo-chip span { display: inline-flex; align-items: center; min-height: 36px; padding: 7px 12px; border: 1px solid transparent; border-radius: var(--r-sm); background: none; color: var(--color-text-secondary); font-size: var(--fs-xs); font-weight: var(--w-strong); cursor: pointer; transition: color var(--ease), box-shadow var(--ease); }
.demo-chip span:hover { color: var(--color-text); }
.demo-chip input:checked + span { color: var(--color-primary); box-shadow: inset 0 -2px 0 var(--color-accent); }
.demo-chip input:focus-visible + span { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.demo-pause { border: 0; background: none; padding: 8px 6px; min-height: 36px; color: var(--color-text-secondary); font: 500 12px var(--font); text-decoration: underline; cursor: pointer; }
.demo-pause:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; border-radius: 4px; }
.demo-note { margin: var(--space-2) 0 0; text-align: center; font-size: var(--fs-xs); color: var(--color-text-secondary); }

/* ── Pestañas del ejemplo (sin JS, los paneles quedan apilados) ── */
.example-tablist { display: flex; gap: 6px; justify-content: center; flex-wrap: wrap; margin: 0 0 18px; }
.example-tablist [role="tab"] { border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-text-secondary); font: 600 13.5px var(--font); padding: 9px 18px; min-height: 40px; border-radius: 20px; cursor: pointer; transition: background var(--ease), border-color var(--ease), color var(--ease); }
.example-tablist [role="tab"][aria-selected="true"] { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-on-accent); }
.example-tablist [role="tab"]:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.example-tabs [role="tabpanel"]:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 4px; border-radius: var(--r); }
.example-tabs [role="tabpanel"] + [role="tabpanel"] { margin-top: 20px; }
.example-tabs [role="tabpanel"][hidden] { display: none; }
.example-table--eval td:not(:first-child):not(:nth-child(2)), .example-table--eval th:not(:first-child):not(:nth-child(2)) { width: 52px; text-align: center; }
.example-table--eval td:nth-child(2), .example-table--eval th:nth-child(2) { width: auto; text-align: left; }
.example-table--eval td:first-child, .example-table--eval th:first-child { width: 34px; text-align: center; }
.membrete { display: flex; gap: 10px; align-items: center; padding: 10px 12px; border: 1px dashed var(--color-border); border-radius: var(--r); color: var(--color-text-tertiary); font-size: 12px; margin: 6px 0 12px; }
.membrete svg { flex: none; color: var(--color-primary-mid); }
.example-firma { margin-top: 22px; padding-top: 24px; border-top: 1px dashed var(--color-border); display: flex; flex-wrap: wrap; gap: 14px 40px; justify-content: space-between; font-size: 12px; line-height: 1.8; color: var(--color-text-tertiary); text-align: center; }
.example-docfoot { margin: 16px 0 0; padding-top: 10px; border-top: 1px solid var(--color-border); font-size: 11px; color: var(--color-text-secondary); }

/* ── Recomendador de plan ── */
.plan-reco { margin: 0 0 26px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--r-lg); padding: 18px 22px; box-shadow: var(--shadow-xs); }
.plan-reco__q { font: 560 16px var(--serif); color: var(--color-heading); margin: 0 0 12px; }
.plan-reco__opts { display: flex; gap: 8px; flex-wrap: wrap; }
.plan-reco__opts button { border: 1px solid var(--color-border); background: var(--color-background); color: var(--color-text); font: 600 13.5px var(--font); padding: 9px 16px; min-height: 40px; border-radius: 20px; cursor: pointer; transition: background var(--ease), border-color var(--ease), color var(--ease); }
.plan-reco__opts button[aria-pressed="true"] { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-on-accent); }
.plan-reco__opts button:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.plan-reco__out { margin: 12px 0 0; font-size: 14px; line-height: 1.6; color: var(--color-text); display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center; }
/* La región live queda siempre renderizada (sin display:none) para que el primer
   anuncio sí se lea (WCAG 4.1.3); vacía colapsa solo su margen. */
.plan-reco__out:empty { margin: 0; }
.plan-reco__cta { padding: 9px 18px; }

/* Admin: embudo de analítica (tabla en una card a todo el ancho de la grilla) */
.funnel-card { grid-column: 1 / -1; }
.funnel-list { width: 100%; border-collapse: collapse; font-size: 13px; margin-top: 6px; }
.funnel-list td { padding: 6px 2px; border-bottom: 1px solid var(--color-border); color: var(--color-text); }
.funnel-list tr:last-child td { border-bottom: 0; }
.funnel-list td.n { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--color-heading); }

/* ── Quill (editor tipo Word) integrado al documento generado ── */
.document-section .ql-container.ql-bubble { font-family: var(--font); font-size: 14px; border: none; }
.document-section .ql-editor { padding: 0; line-height: 1.7; color: var(--color-text); min-height: 28px; }
.document-section .ql-editor ul, .document-section .ql-editor ol { padding-left: 1.4em; margin: 0; }
.document-section .ql-editor.ql-blank::before { left: 0; color: var(--color-text-tertiary); font-style: normal; }
@media print {
  .ql-toolbar, .ql-tooltip { display: none !important; }
  .document-section .ql-editor { padding: 0 !important; }
}

/* ── Barra de formato "Word online" + estado de autoguardado ── */
.format-bar { position: sticky; top: 0; z-index: 5; display: flex; flex-wrap: wrap; align-items: center; gap: 4px; padding: 8px 0; margin-bottom: 8px; border-bottom: 1px solid var(--color-border); background: var(--color-card); }
.format-bar .fmt { min-width: 34px; height: 32px; padding: 0 10px; border: 1px solid var(--color-border); border-radius: 8px; background: transparent; color: var(--color-text); font-size: 13px; line-height: 1; cursor: pointer; }
.format-bar .fmt:hover { background: var(--color-primary-soft); }
.format-bar .fmt.is-active { background: var(--color-primary); color: var(--color-on-accent); border-color: var(--color-primary); }
.format-bar .fmt-sep { width: 1px; height: 20px; background: var(--color-border); margin: 0 4px; }
.autosave-status { margin-left: auto; font-size: 12px; color: var(--color-text-tertiary); min-height: 16px; }
/* Pista visual: el título y los metadatos del documento son editables (renombrar/editar in situ). */
#title[contenteditable="true"] { border-radius: 6px; padding: 2px 6px; margin-left: -6px; cursor: text; transition: background var(--ease), box-shadow var(--ease); }
#title[contenteditable="true"]:hover { background: var(--color-primary-soft); }
#title[contenteditable="true"]:focus { background: var(--color-card); box-shadow: 0 0 0 2px var(--color-primary); outline: none; }
.doc-meta td[contenteditable="true"] { cursor: text; border-radius: 4px; transition: background var(--ease); }
.doc-meta td[contenteditable="true"]:hover { background: var(--color-primary-soft); }
.doc-meta td[contenteditable="true"]:focus { background: var(--color-card); box-shadow: inset 0 0 0 2px var(--color-primary); outline: none; }

@media (max-width: 860px) {
  .landing-hero { grid-template-columns: 1fr; gap: 36px; padding-bottom: 40px; }
  .doc-preview { transform: none; max-width: 460px; }
  .doc-preview::before { display: none; }
  .steps-band, .plans-grid { grid-template-columns: 1fr; }
  .compare-grid, .tools-grid, .casos-grid, .credit-explainer { grid-template-columns: 1fr; }
  .example-meta { grid-template-columns: 1fr 1fr; }
  .closing-card { grid-template-columns: 1fr; text-align: center; padding: 34px 26px; }
  .closing-msg h2, .closing-msg p { margin-left: auto; margin-right: auto; }
  .closing-action { align-items: center; }
}

/* ── Tableta: el escalón que faltaba ──
   El landing saltaba de tres columnas a UNA en cuanto se bajaba de 861px, así que un iPad en
   vertical (768px) recibía el diseño del teléfono: nueve tarjetas de herramienta en fila
   india, y los tres planes uno debajo de otro cuando caben dos al lado. Entre 761 y 860px
   sobra sitio para dos columnas — con `auto-fit` en las rejillas de nueve, la última fila
   queda suelta y se centra sola.
   Va DESPUÉS del bloque de 860 a propósito: misma especificidad, así que gana el último. */
@media (min-width: 761px) and (max-width: 860px) {
  .compare-grid, .tools-grid, .casos-grid, .steps-band, .plans-grid { grid-template-columns: repeat(2, 1fr); }
  /* El paso 3 y el plan 3 quedan solos en su fila: se centran en vez de alinearse a la
     izquierda dejando un hueco. */
  .steps-band > :last-child:nth-child(odd), .plans-grid > :last-child:nth-child(odd) { grid-column: 1 / -1; max-width: calc(50% - var(--space-3)); margin-inline: auto; }
  .credit-explainer { grid-template-columns: 1fr; }
}

/* ── Footer ── */
footer {
  margin-top: 12px;
  padding: 40px 4vw 44px;
  border-top: 1px dashed var(--color-primary-mid);
  /* Era el crema hundido #f2ecdf. Ahora usa el token de superficie hundida, que es el
     mismo papel que los inputs: el pie es fondo, no una banda con identidad propia. */
  background: var(--color-surface-sunken);
  color: var(--color-text-tertiary);
  font-size: 12px;
  text-align: center;
}
.footer-brand {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font: 600 17px/1 var(--serif);
  font-optical-sizing: auto;
  color: var(--color-primary);
  margin-bottom: 14px;
}
.footer-brand svg { color: var(--color-accent-ink); }
footer a {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding: 0 4px;
  color: var(--color-text-secondary);
}
footer a:hover { color: var(--color-primary); }
.footer-note { font-style: italic; }

/* ── Accessibility ── */
.skip-link {
  position: fixed;
  left: 16px;
  top: 12px;
  z-index: 200;
  transform: translateY(-160%);
  padding: 10px 16px;
  background: var(--color-heading);
  color: #fff;
  font-weight: 700;
  border-radius: var(--r-sm);
  transition: transform var(--ease);
  font-size: 14px;
}
.skip-link:focus { transform: translateY(0); }
:focus-visible {
  outline: 2.5px solid var(--color-primary);
  outline-offset: 3px;
}
button:focus-visible, .btn:focus-visible, .outline:focus-visible { outline-offset: 4px; }

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    scroll-behavior: auto !important;
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ── Print ── */
/* ── Avisos efímeros (los apila product-ui.js) ──
   La pila es fija; los avisos son estáticos dentro de ella, así que al cerrar uno los
   demás se acomodan sin recalcular posiciones. La pila no intercepta el puntero
   (pointer-events:none) pero cada aviso sí: el hueco entre avisos deja pasar los clics
   al contenido de debajo. */
/* Anclada a los dos bordes y centrada por flexbox, sin vw ni translateX: `92vw` cuenta la
   barra de desplazamiento y en un móvil estrecho dejaba la pila más ancha que la pantalla. */
.toasts {
  position: fixed; left: 8px; right: 8px; bottom: 28px; z-index: 1000;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  pointer-events: none;
}
.toast { max-width: 460px; }
/* Rejilla de tres columnas —icono | texto | acción— y el cierre en la esquina, en vez de una
   fila que se envuelve: con `flex-wrap` un cuerpo de dos líneas empujaba el botón de acción a
   una cuarta línea y el aviso crecía al doble sin motivo. Aquí la altura es la del texto.
   La entrada es una ANIMACIÓN, no una clase que añade JS en el siguiente frame: un aviso
   disparado con la pestaña de fondo no recibe requestAnimationFrame y se quedaba invisible. */
.toast {
  position: relative;
  width: 100%;
  display: grid; grid-template-columns: 16px minmax(0, 1fr) auto; gap: 10px; align-items: center;
  padding: 13px 32px 13px 18px; background: var(--color-primary); color: var(--color-on-accent);
  font: 600 14px/1.4 var(--font); border-radius: 12px; box-shadow: var(--shadow-lg);
  /* El estado base es VISIBLE y la animación sólo lo hace entrar: al contrario, un aviso
     cuya animación no llega a correr (pestaña de fondo, motor sin animaciones) se quedaría
     invisible para siempre. Aquí lo peor que puede pasar es que aparezca sin deslizarse. */
  animation: toast-in .25s var(--ease);
}
@keyframes toast-in { from { opacity: 0; transform: translateY(14px); } }
.toast.is-leaving { animation: none; opacity: 0; transform: translateY(14px); transition: opacity .2s var(--ease), transform .2s var(--ease); }
@media (prefers-reduced-motion: reduce) {
  .toast { animation-duration: .01ms; }
  .toast.is-leaving { transition-duration: .01ms; }
}
.toast-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.toast-title { font-weight: 700; }
.toast-title + .toast-body { font-weight: 500; opacity: .92; }
.toast-action {
  justify-self: end;
  padding: 6px 12px; border: 1px solid rgba(255,255,255,.55); border-radius: var(--r-sm);
  background: rgba(255,255,255,.14); color: inherit; font: 700 13px/1 var(--font);
  text-decoration: none; cursor: pointer; white-space: nowrap;
}
.toast-action:hover { background: rgba(255,255,255,.26); border-color: #fff; }
.toast-close {
  position: absolute; top: 5px; right: 5px;
  width: 24px; height: 24px; padding: 0;
  border: 0; border-radius: 50%; background: transparent; color: inherit;
  font: 400 20px/1 var(--font); cursor: pointer; opacity: .7;
}
.toast-close:hover { opacity: 1; background: rgba(255,255,255,.16); }
.toast { pointer-events: auto; }
.toast::before {
  content: '';
  width: 16px;
  height: 16px;
  background: #fff;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / contain no-repeat;
}
.toast--error, .toast--danger { background: var(--color-danger); }
.toast--warn { background: var(--color-warning-deep); }
.toast--info { background: var(--color-heading); }
/* El visto sólo corresponde a "hecho": los demás tipos comparten el círculo de atención,
   porque un visto sobre un error es una contradicción que se lee antes que el texto. */
.toast--error::before, .toast--danger::before, .toast--warn::before, .toast--info::before {
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 8v4'/%3E%3Cpath d='M12 16h.01'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 8v4'/%3E%3Cpath d='M12 16h.01'/%3E%3C/svg%3E") center / contain no-repeat;
}

@media print {
  @page { margin: 18mm 16mm; }
  body { background: #ffffff; }
  /* Solo se imprime el documento generado: se oculta cabecera, guía, formulario, ajustes y botones.
     .page-title solo se oculta en páginas de la app (html.is-app, lo marca gate.js): en las
     públicas es el título de cada sección y debe imprimirse. */
  .top, .guia, #form-panel, #refine-section, .toast, .toasts, .no-print,
  .document-toolbar > div:last-child, .document-toolbar .eyebrow { display: none !important; }
  html.is-app .page-title { display: none !important; }
  .wrap { padding: 0 !important; margin: 0 !important; max-width: none; }
  .panel { box-shadow: none !important; border: 0 !important; margin: 0 !important; padding: 0 !important; background: #ffffff !important; }
  #result { margin: 0 !important; }
  .document-toolbar { display: block; margin: 0 0 10px; }
  #document-title, #title { font: 600 27px/1.25 var(--serif); color: #232519; margin: 0; padding-bottom: 10px; border-bottom: 1.5px solid #4f6b47; }
  .document-section { padding: 14px 0; border-bottom: 1px solid #cdcbb8; break-inside: avoid; }
  .document-section:last-child { border-bottom: 0; }
  .document-section h3 { color: #4f6b47; }
  .document-section [contenteditable] { color: #333524; }
  .document-section [contenteditable]:focus { outline: none; }
  .doc-header { margin: 12px 0; }
  .doc-meta { width: 100%; border-collapse: collapse; font-size: 10.5px; break-inside: avoid; }
  .doc-meta th { text-align: left; color: #4f6b47; background: #e6ede2; font-weight: bold; padding: 4px 7px; border: 1px solid #cdcbb8; width: 15%; vertical-align: top; }
  .doc-meta td { padding: 4px 7px; border: 1px solid #cdcbb8; vertical-align: top; }
  .document-section ul { margin: 0; padding-left: 18px; }
  .document-section li { margin-bottom: 4px; }
  table.escala { width: 100%; border-collapse: collapse; font-size: 11px; break-inside: avoid; }
  table.escala th, table.escala td { border: 1px solid #a8a68f; padding: 4px 7px; text-align: left; }
  table.escala th { background: #e6ede2; color: #232519; }
  table.escala td.mark { width: 34px; text-align: center; }
  .doc-footer { margin-top: 14px; padding-top: 8px; border-top: 1px solid #cdcbb8; font-size: 9px; color: #545745; }
  #format-bar, .autosave-status { display: none !important; }
  .doc-letterhead { display: flex; align-items: center; gap: 14px; margin: 0 0 14px; padding-bottom: 8px; border-bottom: 2px solid #4f6b47; break-inside: avoid; }
  .doc-logo { max-height: 56px; max-width: 160px; object-fit: contain; }
  .doc-brand-name { font-weight: bold; color: #232519; font-size: 13px; }
  .doc-brand-role { color: #4f6b47; font-size: 10.5px; }
  .doc-brand-inst { color: #545745; font-size: 10.5px; }
  .doc-signature { margin-top: 26px; break-inside: avoid; }
  .doc-signature-line { width: 220px; border-top: 1px solid #a8a68f; margin-bottom: 4px; }
  .doc-signature-name { font-weight: bold; color: #232519; font-size: 11px; }
  .doc-signature-role { color: #545745; font-size: 9.5px; }
}

/* ═══════════════════════════════════
   Responsive
   ═══════════════════════════════════ */

@media (max-width: 960px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 900px) {
  .grid-2 { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  /* ── La escala sube en el móvil ──
     Medido sobre la portada a 375px: 53 párrafos quedaban a 13,5px y otros nueve entre 10
     y 12,5px. A esa distancia y ese tamaño el texto se lee con esfuerzo, y la recomendación
     asentada para cuerpo en móvil es 16px —también porque por debajo de eso iOS hace zoom
     solo al enfocar un campo—.
     Se redefinen los TOKENS, no los componentes: por eso hacía falta la escala. Un solo
     bloque de seis renglones sube el cuerpo de todo el sitio público de forma coherente,
     en vez de perseguir cincuenta reglas una por una.
     Las dos más pequeñas suben poco a propósito: son rótulos en versalitas y distintivos
     de precio, no texto para leer, y agrandarlos rompería la jerarquía que sostienen. */
  :root {
    /* Este bloque existía porque el escritorio se quedaba corto en móvil. Ahora el
       escritorio ya está donde debe, así que sólo remata el extremo bajo — y hay que
       subirlo: con la escala nueva, --fs-lg en móvil (17) habría quedado POR DEBAJO del
       escritorio (18), que es exactamente lo contrario de lo que este bloque hace. */
    --fs-3xs: 11px;
    --fs-2xs: 12.5px;
    --fs-xs:  14px;
    --fs-sm:  15px;
    --fs-md:  16px;
    --fs-lg:  18px;
  }
  body { font-size: var(--fs-md); }
  .top { height: auto; min-height: 60px; padding: 12px 5vw; }
  .top nav { gap: 4px; font-size: 12px; }
  .top nav a { padding: 6px 8px; }
  .top nav .btn { padding: 8px 12px; }
  .wrap { padding: 40px 5vw; }
  /* En el teléfono el dedo no apunta como el puntero: los botones pasan de 40 a 44px, que es
     el mínimo cómodo asentado para objetivos táctiles (WCAG 2.2 pide 24 como suelo; 44 es la
     recomendación de uso real, y aquí el CTA es la acción que sostiene la página). */
  .btn, .outline { min-height: 44px; }
  .hero-actions { align-items: stretch; }
  .hero-actions a { width: 100%; }
  .grid { grid-template-columns: 1fr; }
  .grid .card { padding: 22px; }
  .panel { padding: 22px 20px; }
  .card  { padding: 22px 20px; }
  .page-title h1 { font-size: 30px; }
  .field input, .field select, .field textarea { font-size: 16px; }
  .chips { grid-template-columns: 1fr 1fr; }
  .chips label { font-size: 12px; }
  .document-toolbar { flex-direction: column; align-items: stretch; }
  .document-toolbar > div { flex-direction: column; }
  .document-toolbar > div .btn,
  .document-toolbar > div .outline { width: 100%; }
  .table-row { display: block; }
  .table-row span:last-child { margin-top: 10px; }
  .help-links { grid-template-columns: 1fr; }
  .top nav .nav-account { border-left: 0; padding-left: 0; margin-left: 0; }
  .top { align-items: flex-start; }
  footer { padding: 22px 5vw; }
}
@media (max-width: 480px) {
  .top nav { width: 100%; justify-content: space-between; }
  .logo { font-size: 19px; }
  .chips { grid-template-columns: 1fr; }
  .wrap { padding-left: 18px; padding-right: 18px; }
  .hero-actions a { font-size: 14px; }
  .top nav a { font-size: 12px; }
}

/* ── Análisis Cualitativo ── */
/* Lista de evaluaciones guardadas seleccionables: checkboxes con scroll propio. */
.eval-pick { display: flex; flex-direction: column; gap: 2px; max-height: 260px; overflow-y: auto; border: 1.5px solid var(--color-border); border-radius: var(--r-sm); background: var(--color-surface-sunken); padding: 8px; }
.eval-pick > label { display: flex; align-items: flex-start; gap: 9px; padding: 7px 9px; border-radius: 6px; font-size: 13px; font-weight: 500; line-height: 1.4; cursor: pointer; margin: 0; }
.eval-pick > label:hover { background: var(--color-primary-soft); }
.eval-pick input[type="checkbox"] { flex: none; margin-top: 2px; accent-color: var(--color-primary); }
.eval-pick__meta { display: block; font-size: 11.5px; color: var(--color-text-secondary); }
/* Evaluación pegada adicional: reusa .field para el textarea; botón quitar a la derecha. */
.eval-extra-item { margin: 10px 0 0; }
.eval-extra-item .eval-extra-remove { align-self: flex-end; padding: 6px 12px; font-size: 12.5px; min-height: 0; }

/* ═══════════════════════════════════════════════════════════
   Modo oscuro
   theme.js fija data-theme en <html> temprano (evita parpadeo); por eso el
   tema se resuelve con un único scope [data-theme] en vez de @media.
   La mayoría de los componentes usan tokens y se adaptan solos; abajo van
   los ajustes puntuales para colores fijos (blancos sobre verde, avisos, etc.).
   ═══════════════════════════════════════════════════════════ */
:root[data-theme="dark"] {
  color-scheme: dark;

  /* Turquesa claro sobre el fondo oscuro: 9.32 sobre la página y 7.38 sobre la tarjeta.
     Aquí el orden se invierte respecto del tema claro —hover y active ACLARAN en vez de
     oscurecer—, porque en oscuro un elemento se acerca al puntero subiendo de brillo.
     Por lo mismo el -ink de oscuro es el mismo tono claro y no uno profundo: la tinta se
     escribe sobre superficies oscuras, así que "tinta" aquí significa clara. */
  --color-primary:        #9CC08F;
  --color-primary-hover:  #B4D0A8;
  --color-primary-active: #C9E0BF;
  /* Sube de #0F312E, que tenía LA MISMA luminancia que --color-card: cualquier relleno con
     este tinte dentro de una tarjeta daba 1.000 y desaparecía. Se descubrió midiendo el
     disco del estado vacío, pero afectaba a todos sus usos como relleno. El valor está
     acotado por los dos lados: tiene que separarse de la tarjeta (1.39) y seguir admitiendo
     --color-primary-ink encima como texto (5.30). */
  --color-primary-soft:   #33452C;
  --color-primary-mid:    #44583C;
  --color-primary-ink:    #9CC08F;
  --color-primary-rgb:    156 192 143;

  /* El violeta se aclara igual que el turquesa: 5.68 sobre la tarjeta, 7.16 sobre la página,
     y la tinta oscura encima del relleno da 7.37. */
  --color-ai:        #A899F0;
  --color-ai-hover:  #BFB3F5;
  --color-ai-active: #D2C9F9;
  --color-ai-mid:    #4A3F82;
  --color-ai-soft:   #241E45;
  --color-ai-ink:    #A899F0;

  /* El amarillo es el mismo que en claro —es relleno en los dos temas—; lo que cambia es
     el tinte de fondo y la tinta. */
  --color-accent:        #E8A87C;
  /* Sube de #3A2E10 por lo mismo que el tinte primario: daba 1.057 contra --color-card y el
     disco de la tercera promesa —que no tiene ni borde ni texto dentro, sólo el icono— se
     perdía en la tarjeta. Ahora 1.273, y la tinta cálida encima sigue en 7.78. */
  --color-accent-soft:   #4B3020;
  --color-accent-ink:    #F3C9A8;
  --color-coral:      #D08A6F;
  --color-coral-ink:  #D08A6F;
  --color-on-accent:      #12140D;

  /* La familia de estado NO estaba redefinida aquí: sus trece valores oscuros vivían
     escritos a mano en trece reglas de ajuste. Declararlos hace que .notice-*, .status-* y
     .outline.danger se resuelvan solos y esas reglas puedan borrarse. */
  --color-danger:       #E78D82;
  --color-danger-hover: #F0A49A;
  --color-danger-border:  #6E2A24;
  --color-danger-soft:    #391A16;
  --color-danger-ink:   #F3B7B0;
  --color-warning:         #E8B657;
  --color-warning-strong:  #E8B657;
  --color-warning-deep:    #8A5A06;
  --color-warning-soft:      #33280F;
  --color-warning-ink:     #F0D69A;
  --color-success-soft:        #14301C;
  --color-success-ink:       #BFE7C6;

  /* La familia de texto deja de ser crema cálida y pasa a los mismos grises azulados del
     tema claro, invertidos. Sobre la tarjeta: 11.36 el principal, 6.42 el secundario y 5.54
     el terciario, que además sube a 6.67 sobre un input, su peor fondo en claro. */
  --color-heading:        #F2F1E8;
  --color-text:           #E4E3D7;
  --color-text-secondary: #ADAF9C;
  --color-text-tertiary:  #9EA08D;

  /* Los mismos tres planos que en claro, y en el mismo orden: la página hundida, el panel
     encima y el papel elevado. Aquí --color-card era IGUAL a --color-surface, así que en oscuro las
     tarjetas tampoco tenían contorno propio; ahora sube un escalón. En oscuro un plano se
     eleva aclarándose, al revés que en claro, donde se eleva emblanqueciéndose. */
  /* Los mismos cuatro planos que en claro y en el mismo orden, pero elevándose al revés:
     en oscuro un plano sube ACLARÁNDOSE. La separación es incluso mejor que en claro —
     panel sobre página 1.122 y tarjeta sobre panel 1.125, contra el 1.051 de allá — así que
     aquí el contorno carga menos peso; aun así el borde da 1.450 sobre la tarjeta.
     Ojo con el hundido: en claro va por debajo del panel, y aquí también, pero eso significa
     que es MÁS OSCURO que la tarjeta (1.204), no más claro como estaba antes. */
  --color-border:         #454738;
  --color-border-strong:  #5F6250;
  --color-background:     #191A14;
  --color-surface:        #23241C;
  --color-surface-sunken: #1E1F18;
  --color-card:           #2C2D23;

  --shadow-xs: 0 1px 2px rgba(0,0,0,.35);
  --shadow-sm: 0 2px 8px rgba(0,0,0,.4), 0 1px 2px rgba(0,0,0,.35);
  --shadow:    0 5px 20px rgba(0,0,0,.45), 0 1px 4px rgba(0,0,0,.4);
  --shadow-lg: 0 14px 40px rgba(0,0,0,.55), 0 2px 8px rgba(0,0,0,.4);

  --sk-bg:    #3A3B2E;
  --sk-glint: rgba(255,255,255,.055);
}
/* Sin grano de papel sobre el fondo oscuro. */
:root[data-theme="dark"] body { background-image: none; }
/* Aquí había once reglas invirtiendo a mano el texto de diez selectores que clavaban
   #fff sobre el verde. Con --color-on-accent —blanco en claro, tinta en oscuro— cada selector
   se resuelve solo y el bloque entero desaparece. Sólo queda el ::before del aviso, que
   pinta un icono por máscara y no hereda color. */
:root[data-theme="dark"] .toast--success::before { background: var(--color-on-accent); }
/* Los tres avisos oscuros (error, aviso, informativo) llevan texto claro: su fondo es
   oscuro también en tema claro, así que no se invierten. */
:root[data-theme="dark"] .toast--info { background: var(--color-surface-sunken); color: var(--color-text); }
:root[data-theme="dark"] .toast--success .toast-action { border-color: rgba(11,22,32,.45); background: rgba(11,22,32,.1); }
:root[data-theme="dark"] .toast--success .toast-action:hover { background: rgba(11,22,32,.2); border-color: var(--color-on-accent); }
/* Aquí vivían ocho reglas: los tres .notice-*, los dos .outline.danger, .danger-zone y
   .danger-confirm. Todas repetían en oscuro lo que la regla clara ya decía, solo que con
   otros valores. Al declarar la familia de estado en el bloque de tokens oscuros, las reglas
   claras se resuelven solas y estas sobran. La única que queda es la del hover, porque su
   fondo no es --color-danger-soft sino un rojo aún más apagado. */
:root[data-theme="dark"] .outline.danger:hover { background: #2a1512; }
/* Skip link (visible al enfocar con teclado): en claro es tinta oscura con texto blanco;
   en oscuro se invierte a relleno turquesa. Es lo único que queda de ese grupo. */
:root[data-theme="dark"] .skip-link { background: var(--color-primary); color: var(--color-on-accent); }
/* La banda es la misma en los dos temas, así que lo de dentro tampoco puede seguir al tema:
   en oscuro --color-accent-soft es un marrón oscuro y sobre el turquesa profundo daba 1.83
   en el eyebrow. Redeclarar el token en el ámbito de la banda arregla de una vez el eyebrow
   (6.08) y el hover del botón, sin una regla por hijo. Al cambiar el acento hay que mover
   este valor con él. */
:root[data-theme="dark"] .closing-card { --color-accent-soft: #f2e6cf; }
/* El círculo del check se recorta sobre el fondo sage de la tarjeta recomendada. */
:root[data-theme="dark"] .plan-tier.featured .tier-features li::before { background: var(--color-background); }

/* ── Botón de cambio de tema (inyectado por theme.js en la barra) ── */
.theme-toggle {
  flex: none;
  margin-left: 8px;
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1.5px solid var(--color-primary-mid);
  border-radius: var(--r-sm);
  background: var(--color-surface);
  color: var(--color-primary);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--ease), border-color var(--ease), color var(--ease);
}
.theme-toggle:hover { background: var(--color-primary-soft); border-color: var(--color-primary); }

/* ── Recursos visuales imprimibles ──
   Los rótulos son texto real del navegador, nunca texto dentro de la ilustración:
   así salen bien escritos en español, se editan y los lee un lector de pantalla. */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.recurso-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 18px; margin-top: 16px; }
.recurso-item { margin: 0; display: flex; flex-direction: column; gap: 8px; padding: 12px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--r); box-shadow: var(--shadow-xs); }
.recurso-item img { width: 100%; aspect-ratio: 1 / 1; object-fit: contain; background: #fff; border-radius: var(--r-sm); }
.recurso-item figcaption { margin: 0; }
.recurso-item .field { margin: 0; }
.recurso-item .actions { margin-top: auto; }
@media print {
  .recurso-item { break-inside: avoid; border: 0; box-shadow: none; }
  .recurso-item .actions, #workflow, .document-toolbar > div:last-child { display: none !important; }
}

/* ── Color de curso ──
   Se guarda el NOMBRE del token en la base (courses.color), nunca un valor CSS: así el tema
   claro y el oscuro lo resuelven distinto y nada de lo que escriba la usuaria entra en una
   hoja de estilo. Cada token define tres variables y quien las use decide para qué:
     --c-solid → el punto, la barra lateral, el trazo. Color pleno.
     --c-soft  → el fondo de una ficha o un chip.
     --c-ink   → el texto sobre --c-soft (contraste AA verificado en ambos temas).
   Sin data-color, --c-* no existe y cada regla cae en su valor por defecto (el verde de
   marca), así que los cursos creados antes de la migración 023 se ven bien igual. */
[data-color="verde"]     { --c-solid: #4F6B47; --c-soft: #E6EDE2; --c-ink: #3B5235; }
[data-color="miel"]      { --c-solid: #A8721A; --c-soft: #FAF0DC; --c-ink: #7A4E00; }
[data-color="terracota"] { --c-solid: #B5654F; --c-soft: #F8E7E0; --c-ink: #8E4530; }
[data-color="indigo"]    { --c-solid: #2F5E86; --c-soft: #E3EEF4; --c-ink: #1F5474; }
[data-color="ciruela"]   { --c-solid: #8A3A66; --c-soft: #F7E6EF; --c-ink: #762F56; }
[data-color="pizarra"]   { --c-solid: #5A5A4C; --c-soft: #EDEDE4; --c-ink: #494A3D; }
:root[data-theme="dark"] [data-color="verde"]     { --c-solid: #9CC08F; --c-soft: #2A3A24; --c-ink: #B4D0A8; }
:root[data-theme="dark"] [data-color="miel"]      { --c-solid: #E8B657; --c-soft: #3A2E10; --c-ink: #F0D69A; }
:root[data-theme="dark"] [data-color="terracota"] { --c-solid: #D08A6F; --c-soft: #3A2318; --c-ink: #EEBFA8; }
:root[data-theme="dark"] [data-color="indigo"]    { --c-solid: #8FB4CE; --c-soft: #17262F; --c-ink: #BBD4E4; }
:root[data-theme="dark"] [data-color="ciruela"]   { --c-solid: #D592B6; --c-soft: #301B26; --c-ink: #EFC4DA; }
:root[data-theme="dark"] [data-color="pizarra"]   { --c-solid: #A8A894; --c-soft: #26271F; --c-ink: #D2D2C2; }
.color-dot { flex: none; width: 10px; height: 10px; border-radius: 50%; background: var(--c-solid, var(--color-primary)); }

/* ── Calendario ──
   La rejilla imita el gesto de un calendario de escritorio: cuadrícula de líneas finas (un
   gap de 1px sobre un fondo del color de la línea, no bordes por celda, que se duplican en
   los encuentros), los días del mes vecino en gris en vez de huecos vacíos —así la primera
   semana se lee como semana y no como una fila mutilada— y el día de hoy con su número en
   un círculo pleno.
   Tres vistas comparten la misma rejilla: `mes` (6 filas), `semana` (una fila alta) y
   `agenda` (lista vertical de los días con contenido, que es también la vista de móvil).
   El carril de la derecha es el día seleccionado: sustituye al formulario suelto de "elige
   la fecha y luego pulsa Agendar" por el gesto natural — toco el día, agendo en él. */
.cal-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px; margin-bottom: 16px; }
.cal-nav { display: flex; align-items: center; gap: 8px; }
.cal-nav h2 { margin: 0 0 0 6px; font-size: 20px; color: var(--color-heading); text-transform: capitalize; letter-spacing: -.01em; }
.cal-arrow {
  flex: none; width: 34px; height: 34px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--color-border); border-radius: 50%; background: var(--color-surface);
  color: var(--color-text-secondary); font: 400 19px/1 var(--font); cursor: pointer;
  transition: background var(--ease), color var(--ease), border-color var(--ease);
}
.cal-arrow:hover { background: var(--color-primary-soft); border-color: var(--color-primary-mid); color: var(--color-primary); }
.cal-toolbar-right { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.cal-filtro { margin: 0; max-width: 240px; }
/* Conmutador de vista: un grupo de botones con aria-pressed, no radios disfrazados. El
   estado real es aria-pressed y el CSS lo lee de ahí, así que nunca se desincronizan. */
.cal-views { display: inline-flex; border: 1px solid var(--color-border); border-radius: var(--r-sm); overflow: hidden; }
.cal-view {
  padding: 8px 14px; border: 0; background: var(--color-surface); color: var(--color-text-secondary);
  font: 600 13px/1 var(--font); cursor: pointer;
}
.cal-view + .cal-view { border-left: 1px solid var(--color-border); }
.cal-view:hover { background: var(--color-primary-soft); color: var(--color-primary); }
.cal-view[aria-pressed="true"] { background: var(--color-primary); color: var(--color-on-accent); }

.cal-body { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 20px; align-items: start; }
.cal-main { min-width: 0; }
.cal-grid {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px; background: var(--color-border); border: 1px solid var(--color-border); border-radius: var(--r);
  overflow: hidden;
}
.cal-grid[hidden], .cal-agenda[hidden] { display: none; }
.cal-dow { background: var(--color-surface-sunken); font: 600 11px/1 var(--font); color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: .06em; text-align: center; padding: 9px 0; }
.cal-cell { min-height: 104px; padding: 5px; background: var(--color-surface); display: flex; flex-direction: column; gap: 3px; cursor: pointer; }
.cal-cell:hover { background: var(--color-surface-sunken); }
.cal-cell.is-outside { background: var(--color-background); }
.cal-cell.is-selected { box-shadow: inset 0 0 0 2px var(--color-primary); }
.cal-daynum {
  align-self: flex-start; min-width: 24px; height: 24px; padding: 0 6px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 12px; background: transparent;
  font: 600 12.5px/1 var(--font); color: var(--color-text-secondary); cursor: pointer;
}
.cal-daynum:hover { background: var(--color-primary-soft); color: var(--color-primary); }
.cal-cell.is-outside .cal-daynum { color: var(--color-text-tertiary); }
.cal-cell.is-today .cal-daynum { background: var(--color-primary); color: var(--color-on-accent); }
/* Semana: una sola fila, así que la celda puede respirar y mostrarlo todo sin "+N más". */
.cal-grid.is-week .cal-cell { min-height: 340px; }
.cal-grid.is-week .cal-dow { padding: 9px 4px; text-transform: none; letter-spacing: 0; font-size: 12px; }

.cal-item {
  display: flex; align-items: baseline; gap: 5px; width: 100%; text-align: left;
  border: 0; border-left: 3px solid var(--c-solid, var(--color-primary)); border-radius: 4px;
  padding: 3px 6px; font: 500 11.5px/1.35 var(--font);
  color: var(--c-ink, var(--color-heading)); background: var(--c-soft, var(--color-primary-soft));
  cursor: pointer;
}
.cal-item:hover { filter: brightness(.97); }
.cal-item__block { flex: none; font-weight: 700; opacity: .75; font-size: 10.5px; text-transform: uppercase; }
.cal-item__title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Realizada: el título tachado y el fondo apagado. Sigue visible a propósito — el valor del
   mes está en ver lo que YA se trabajó, no sólo lo que falta. */
.cal-item.is-executed { background: transparent; color: var(--color-text-secondary); }
.cal-item.is-executed .cal-item__title { text-decoration: line-through; }
.cal-item.is-cancelled { background: transparent; border-left-color: var(--color-border); color: var(--color-text-tertiary); }
.cal-item.is-cancelled .cal-item__title { text-decoration: line-through; }
.cal-item.is-task { background: transparent; border: 1px dashed var(--color-border); border-left: 3px solid var(--color-accent); color: var(--color-text-secondary); cursor: default; }
.cal-more { align-self: flex-start; margin-top: auto; border: 0; background: none; padding: 2px 6px; font: 600 11px/1 var(--font); color: var(--color-text-secondary); cursor: pointer; }
.cal-more:hover { color: var(--color-primary); text-decoration: underline; }

/* Vista agenda: la lista de los días con algo. Es la misma vista que se usa en móvil, así
   que el mes deja de ser una rejilla ilegible de 40px y pasa a ser una lista de días. */
.cal-agenda { display: flex; flex-direction: column; gap: 2px; }
.cal-agenda-day { display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 14px; padding: 12px 6px; border-top: 1px solid var(--color-border); }
.cal-agenda-day:first-child { border-top: 0; }
.cal-agenda-day.is-today { background: var(--color-primary-soft); border-radius: var(--r-sm); border-top-color: transparent; }
.cal-agenda-date { font: 600 13px/1.4 var(--font); color: var(--color-heading); text-transform: capitalize; }
.cal-agenda-date small { display: block; font-weight: 500; color: var(--color-text-tertiary); text-transform: none; }
.cal-agenda-items { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.cal-agenda .cal-item { font-size: 13px; padding: 6px 10px; }

/* Carril del día seleccionado */
.cal-rail { background: var(--color-surface-sunken); border: 1px solid var(--color-border); border-radius: var(--r); padding: 16px; }
.cal-rail h3 { margin: 0 0 2px; font-size: 15px; color: var(--color-heading); text-transform: capitalize; }
.cal-rail-sub { margin: 0 0 14px; font-size: 12px; color: var(--color-text-tertiary); }
.cal-rail-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }
.cal-rail-item { background: var(--color-surface); border: 1px solid var(--color-border); border-left: 3px solid var(--c-solid, var(--color-primary)); border-radius: var(--r-sm); padding: 9px 10px; }
.cal-rail-item strong { display: block; font-size: 13px; line-height: 1.35; color: var(--color-heading); }
.cal-rail-item small { display: block; margin-top: 2px; font-size: 11.5px; color: var(--color-text-tertiary); }
.cal-rail-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.cal-rail-actions button, .cal-rail-actions a { padding: 5px 9px; font-size: 11.5px; }
.cal-rail-empty { margin: 0 0 16px; font-size: 12.5px; color: var(--color-text-tertiary); }
/* "Mover": se despliega dentro de la propia ficha en vez de abrir un diálogo, porque el dato
   que cambia (la fecha) pertenece a esa ficha y no a la página. */
.cal-mover { margin-top: 9px; padding-top: 9px; border-top: 1px dashed var(--color-border); }
.cal-mover[hidden] { display: none; }
.cal-mover .field { margin: 0 0 8px; font-size: 11.5px; }
.cal-mover .field input, .cal-mover .field select { padding: 6px 8px; font-size: 12.5px; }
.cal-mover .btn { width: 100%; padding: 7px 10px; font-size: 12px; }
.cal-rail form { border-top: 1px solid var(--color-border); padding-top: 14px; }
.cal-rail form .field { margin: 0 0 10px; }
.cal-rail form .field select, .cal-rail form .field input { padding: 8px 10px; font-size: 13px; }
.cal-rail form .btn { width: 100%; }

/* Leyenda de colores de curso */
.cal-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 14px 0 0; padding: 0; list-style: none; }
.cal-legend li { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--color-text-secondary); }

.cal-buckets { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 28px; padding-top: 22px; border-top: 1px solid var(--color-border); }
.cal-bucket h2 { font-size: 16px; margin: 0 0 4px; color: var(--color-heading); }
.cal-bucket > .small-note { margin: 0 0 12px; }
.cal-bucket .table-row { border: 1px solid var(--color-border); border-radius: var(--r-sm); margin-bottom: 6px; }
/* El botón que abre el resto de la cubeta. Ocupa el ancho de una fila y no el de un botón
   suelto: está en la posición donde continuaría la lista, así que su caja tiene que decir
   "aquí sigue esto" y no parecer una acción más de la última fila. */
.cal-bucket-mas { width: 100%; justify-content: center; margin-top: 2px; font-size: 12.5px; }

@media (max-width: 1040px) {
  /* El carril pasa a ir DEBAJO de la rejilla, no a la derecha: a 900px una columna de 300px
     dejaría el mes en 7 columnas de 60px. */
  .cal-body { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 700px) {
  .cal-toolbar { gap: 10px; }
  .cal-nav h2 { font-size: 17px; }
  .cal-filtro { max-width: none; flex: 1 1 100%; }
  .cal-agenda-day { grid-template-columns: 76px minmax(0, 1fr); gap: 10px; }
  .cal-buckets { grid-template-columns: 1fr; }
  /* En móvil la vista por omisión es `agenda` (lo decide calendario-page.js), pero mes y
     semana siguen disponibles: en vez de encogerse a columnas de 30px —ilegibles— la rejilla
     conserva su ancho mínimo y se desplaza DENTRO de su caja. La página nunca se desplaza en
     horizontal; el mes sí. */
  .cal-main { overflow-x: auto; }
  .cal-grid { min-width: 620px; }
  .cal-grid.is-week .cal-cell { min-height: 200px; }
}

/* ── Mis cursos ──
   Fichas y no filas: un curso tiene color, nivel, establecimiento y año, y en una .table-row
   todo eso terminaba dentro de un <small> con cuatro separadores "·". La barra superior de la
   ficha es el color del curso, el mismo que identifica sus experiencias en el calendario. */
.curso-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 16px; }
.curso-card {
  display: flex; flex-direction: column; gap: 4px;
  padding: 15px 16px 14px; background: var(--color-surface);
  border: 1px solid var(--color-border); border-top: 3px solid var(--c-solid, var(--color-primary));
  border-radius: var(--r); box-shadow: var(--shadow-xs);
}
.curso-card h3 { margin: 0; font-size: 15.5px; line-height: 1.3; color: var(--color-heading); }
.curso-nivel { font-size: 12.5px; font-weight: 600; color: var(--c-ink, var(--color-primary)); }
.curso-meta { margin: 0; font-size: 12px; line-height: 1.5; color: var(--color-text-tertiary); }
.curso-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: auto; padding-top: 12px; }
.curso-actions .outline, .curso-actions a { padding: 6px 11px; font-size: 12px; }
.curso-card.is-archived { opacity: .72; border-top-color: var(--color-border); }
/* Selector de color: radios reales con el círculo como etiqueta. El foco se ve porque el
   :focus-visible del input pinta el anillo sobre el círculo hermano. */
.color-pick { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.color-opt { position: relative; display: inline-flex; }
.color-opt input { position: absolute; inset: 0; opacity: 0; cursor: pointer; margin: 0; }
.color-opt span {
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--c-solid); border: 2px solid transparent; box-shadow: inset 0 0 0 2px var(--color-surface);
  cursor: pointer;
}
.color-opt span:hover, .color-opt input:hover + span { border-color: var(--color-text-secondary); }
.color-opt input:checked + span { border-color: var(--color-heading); }
.color-opt input:focus-visible + span { outline: 2px solid var(--color-primary); outline-offset: 2px; }

/* ── Campana de avisos (la inyecta avisos.js en la barra) ──
   Su `order` dentro del <nav> se declara arriba, junto al del resto de la barra: el orden
   visual no depende del orden del DOM porque cada script inyecta su botón cuando termina. */
.nav-bell { position: relative; flex: none; margin-left: 8px; }
.nav-bell__btn {
  width: 38px; height: 38px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--color-primary-mid); border-radius: var(--r-sm);
  background: var(--color-surface); color: var(--color-primary); cursor: pointer;
  transition: background var(--ease), border-color var(--ease);
}
.nav-bell__btn:hover { background: var(--color-primary-soft); border-color: var(--color-primary); }
.nav-bell__count {
  position: absolute; top: -5px; right: -5px;
  min-width: 18px; height: 18px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
  /* La tinta no puede ser #fff: en oscuro la familia de peligro se aclara —tiene que
     leerse COMO TEXTO sobre fondo oscuro en otros cinco sitios—, así que el blanco encima
     daba 2.46. Con el token que sigue al tema: 6.57 en claro y 7.37 en oscuro. */
  border-radius: 9px; background: var(--color-danger); color: var(--color-on-accent);
  font: 700 10.5px/1 var(--font); box-shadow: 0 0 0 2px var(--color-surface);
}
.nav-bell__count[hidden] { display: none; }
.avisos-panel {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 60;
  width: min(94vw, 366px); max-height: min(70vh, 560px); overflow-y: auto;
  padding: 14px; background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--r); box-shadow: var(--shadow-lg);
}
.avisos-panel[hidden] { display: none; }
.avisos-panel h2 { margin: 0 0 10px; font-size: 14px; color: var(--color-heading); }
.avisos-group { margin: 0 0 14px; }
.avisos-group:last-of-type { margin-bottom: 0; }
.avisos-group > .eyebrow { margin: 0 0 6px; font-size: 10px; }
.aviso {
  display: block; width: 100%; text-align: left;
  padding: 9px 10px; margin-bottom: 5px;
  border: 1px solid var(--color-border); border-left: 3px solid var(--c-solid, var(--color-primary));
  border-radius: var(--r-sm); background: var(--color-surface); color: var(--color-text);
  font: 500 12.5px/1.4 var(--font); text-decoration: none; cursor: pointer;
}
.aviso:hover { background: var(--color-surface-sunken); }
.aviso strong { display: block; font-size: 13px; color: var(--color-heading); }
.aviso small { display: block; margin-top: 1px; color: var(--color-text-tertiary); font-size: 11.5px; }
.aviso.is-late { border-left-color: var(--color-danger); }
.aviso.is-task { border-left-color: var(--color-accent); }
.avisos-empty { margin: 0; font-size: 12.5px; color: var(--color-text-tertiary); }
.avisos-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--color-border); }
.avisos-foot a { font-size: 12.5px; font-weight: 600; }
.avisos-foot button { border: 0; background: none; padding: 0; font: 600 12px/1 var(--font); color: var(--color-text-secondary); cursor: pointer; text-decoration: underline; }
.avisos-foot button:hover { color: var(--color-primary); }
/* La fila del permiso del navegador lleva un texto largo (dice qué hace, o dónde se
   desbloquea) y a veces una respuesta a la última pulsación. Con el `space-between` del pie
   a secas, esas tres piezas se apretaban en una línea. Aquí el texto manda y la respuesta
   baja entera. `align-items: start` porque el botón debe quedar arriba, junto a la primera
   línea del texto, y no centrado contra un párrafo de cuatro renglones. */
.avisos-permiso { flex-wrap: wrap; align-items: flex-start; }
.avisos-permiso > .avisos-empty { flex: 1 1 12rem; line-height: 1.45; }
.avisos-permiso > button { flex: 0 0 auto; padding-top: 2px; }
.avisos-permiso__nota { flex-basis: 100%; color: var(--color-text-secondary); }
/* ── Los enlaces del panel NO son pestañas de la barra ──
   El panel cuelga de la campana, y la campana vive dentro del <nav>. Así que a cada aviso
   —que es un <a>— le caía encima `.top nav a:not(.btn):not(.outline)` (0,3,2), que le ganaba
   a `.aviso` (0,1,0) e imponía lo que necesita una pestaña: `display: inline-flex`,
   `white-space: nowrap`, su relleno y su altura mínima.
   El resultado era un aviso en UNA sola línea que no cabía: "Tienes 16 documentos sin
   fechaAgéndalos para que aparez…", con el título y el pie pegados en horizontal en vez de
   uno debajo del otro. Y como .avisos-panel lleva `overflow-y: auto`, el otro eje deja de ser
   `visible` y pasa a `auto` por la propia definición de overflow: el panel entero se ganaba
   una barra de desplazamiento horizontal, que es lo que se veía asomar debajo.
   Estos dos selectores empatan en (0,3,2) y ganan por orden de origen —van 2.500 líneas más
   abajo—, que es el mismo mecanismo que la sección de paneles del menú ya documenta. No basta
   con reponer `display`: hay que deshacer TODO lo que impuso la regla de pestaña. */
.top nav .avisos-panel a.aviso {
  display: block; white-space: normal;
  padding: 9px 10px; min-height: 0;
  font: 500 12.5px/1.4 var(--font); color: var(--color-text); background: var(--color-surface);
}
.top nav .avisos-panel a.aviso:hover { background: var(--color-surface-sunken); color: var(--color-text); }
.top nav .avisos-panel .avisos-foot a {
  display: inline; white-space: normal;
  padding: 0; min-height: 0;
  font-size: 12.5px; font-weight: 600; color: var(--color-primary); background: none;
}
@media (max-width: 960px) {
  /* En móvil la barra es un cajón: el panel se ancla a la ventana, no al botón, porque la
     campana queda pegada al borde derecho y el panel se saldría de la pantalla. */
  .avisos-panel { position: fixed; right: 8px; left: 8px; top: 62px; width: auto; }
}
@media print { .nav-bell { display: none !important; } }

/* Próximas fechas en el panel de inicio */
.proximas { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.proximas li { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; color: var(--color-text); }
.proximas li b { flex: none; min-width: 64px; font-weight: 600; color: var(--color-text-secondary); text-transform: capitalize; }
.proximas li span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── Esqueletos de carga ──
   El problema que resuelven no es de adorno: estas pantallas se dibujan en dos tiempos —el
   cascarón llega con el HTML y el contenido con la API— y en medio quedaba un hueco con la
   forma de un error. La portada mostraba el título sobre un vacío y luego tres tarjetas
   empujaban media página hacia abajo; el calendario enseñaba una rejilla sin días y un carril
   de 40px que crecía a 400. Reservar el sitio exacto con un gris que se mueve dice "esto
   viene en camino" y, sobre todo, deja quieto lo que ya está.
   Regla: un esqueleto NUNCA sustituye a un estado vacío. Mientras se carga no se puede
   afirmar "no tienes nada" —todavía no se sabe—, así que el vacío se pinta cuando la
   respuesta llegó, y sólo entonces. */
.sk { display: block; position: relative; overflow: hidden; border-radius: var(--r-sm); background: var(--sk-bg); }
.sk::after {
  content: ''; position: absolute; inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, var(--sk-glint), transparent);
  animation: sk-glint 1.5s ease-in-out infinite;
}
@keyframes sk-glint { to { transform: translateX(100%); } }
/* Un brillo que recorre la pantalla en bucle es exactamente lo que 2.3.3 pide poder apagar. */
@media (prefers-reduced-motion: reduce) { .sk::after { animation: none; } }

/* Las tres tarjetas del resumen de inicio. Las alturas están MEDIDAS sobre las de verdad, no
   estimadas: 179px las dos primeras y 262 la de próximas fechas, que lleva además un botón y
   es la que estira la fila entera en escritorio. Apiladas en el teléfono cada una responde por
   su altura, y por eso son dos números y no uno. */
.card--sk { display: flex; flex-direction: column; gap: 12px; min-height: 190px; }
/* La que lleva botón mide 262 y es la que estira la fila entera en escritorio. Va por clase y
   no por :nth-child(3) como al principio: en el perfil las tres tarjetas son simples, y una
   regla posicional le reservaba 70px de más a la tercera sin que nadie se lo pidiera. Quien
   pone la cuarta pieza es quien declara la clase. */
.card--sk-cta { min-height: 262px; }
.card--sk .sk:nth-child(1) { width: 74px; height: 10px; }
.card--sk .sk:nth-child(2) { width: 58%;  height: 22px; }
.card--sk .sk:nth-child(3) { width: 86%;  height: 13px; }
/* La cuarta pieza sólo la tiene la tercera tarjeta, que es la única con botón. Va abajo del
   todo porque ahí es donde aparecerá: si el hueco reservado no tiene la forma de lo que viene,
   el relleno se nota como un cambio y no como el mismo bloque terminándose de dibujar. */
.card--sk .sk:nth-child(4) { width: 148px; height: 40px; margin-top: auto; border-radius: var(--r-pill); }

/* Calendario. El bloque de la rejilla ocupa las siete columnas: es el hueco de las seis
   semanas, que es el desplazamiento más grande de toda la app. */
.cal-grid > .sk--grid { grid-column: 1 / -1; height: 659px; border-radius: 0; }
.cal-grid.is-week > .sk--grid { height: 370px; }
.cal-rail > .sk--rail { height: 470px; }
/* La altura de UNA fila de verdad (.table-row mide unos 70px): no se puede acertar el número
   de filas que vendrán, pero sí lo que ocupa cada una. La usan el historial, las cubetas del
   calendario y el cuerpo del ejemplo mientras se pide. */
.sk--row { height: 71px; }
.sk--row + .sk--row { margin-top: 8px; }
/* Una tarjeta de curso mide 238px con sus tres líneas y sus cuatro botones. */
.sk--curso { height: 238px; }
/* La ficha de suscripción, 306px con el relleno del panel. Las piezas de dentro siguen su
   forma —encabezado, barra de créditos, nota, botón— para que el relleno se lea como el mismo
   bloque terminándose de dibujar y no como otra cosa que aparece en su lugar.
   Cada pieza lleva su clase en vez de un :nth-child: el primer hijo del panel es el texto
   "Cargando tu suscripción…" para quien usa lector de pantalla —invisible pero presente en el
   DOM—, así que contar posiciones daría de lleno en el elemento equivocado. */
.sub-summary--sk { display: flex; flex-direction: column; gap: 18px; min-height: 306px; }
.sk--sub-head { width: 260px; height: 46px; }
.sk--sub-bar  { width: 100%;  height: 30px; }
.sk--sub-note { width: 240px; height: 16px; }
.sk--sub-cta  { width: 168px; height: 44px; margin-top: auto; border-radius: var(--r-pill); }
@media (max-width: 700px) {
  /* En móvil la vista por omisión es la agenda (leerUrl()), así que la rejilla ni siquiera se
     va a mostrar: reservar 631px sería inventar un salto que no existe. */
  .cal-grid > .sk--grid { height: 220px; }
}

/* El saldo de la barra. El chip existe desde el primer pintado con el ancho que tendrá, así
   que el número aparece dentro de su sitio en vez de empujar la barra entera. La chispa se va
   con `color: transparent` — se pinta con currentColor a través de una máscara. */
.nav-plan .status-pill.is-sk { min-width: 100px; background: var(--sk-bg); color: transparent; }
@media (max-width: 700px) { .nav-plan .status-pill.is-sk { min-width: 74px; } }

/* ── El ejemplo del documento ──
   Va justo bajo la línea de créditos de cada herramienta, que es donde la pregunta aparece:
   se acaba de leer "cuesta 2 créditos" y lo siguiente que uno quiere saber es a cambio de
   qué. Cerrado por omisión —quien ya conoce la herramienta no necesita verlo cada vez— y con
   el mismo desplegable que las preguntas frecuentes, que ya es el gesto de "ábreme" del
   sitio.
   La hoja de dentro se desplaza en horizontal por su cuenta: las dos evaluaciones son tablas
   anchas con medidas en puntos —las mismas del Word y del Excel— y no se pueden estrechar sin
   dejar de ser lo que se entrega. */
.ejemplo { margin: 0 0 var(--space-4); border: 1px solid var(--color-border); border-radius: var(--r-sm); background: var(--color-surface); }
.ejemplo > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; gap: var(--space-2);
  min-height: 44px; padding: 10px var(--space-4);
  font: 600 var(--fs-sm)/1.4 var(--font); color: var(--color-primary-ink);
}
.ejemplo > summary::-webkit-details-marker { display: none; }
.ejemplo > summary::after {
  content: ''; flex: none; width: 13px; height: 13px; background: var(--color-primary);
  -webkit-mask: var(--icon-caret) center / contain no-repeat;
  mask: var(--icon-caret) center / contain no-repeat;
  transition: transform var(--ease);
}
.ejemplo[open] > summary::after { transform: rotate(180deg); }
.ejemplo > summary:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; border-radius: var(--r-sm); }
@media (prefers-reduced-motion: reduce) { .ejemplo > summary::after { transition: none; } }
/* La nota y el cuerpo sólo existen con el desplegable abierto: cerrado, <details> los oculta
   por sí mismo, así que no hacen falta reglas para eso. */
.ejemplo > .small-note { margin: 0 var(--space-4) var(--space-3); }
.ejemplo-cuerpo { margin: 0 var(--space-4) var(--space-4); overflow-x: auto; }
/* La hoja: fondo de papel y ancho de documento, para que se lea como lo que va a llegar y no
   como una sección más del formulario. */
.ejemplo-hoja {
  background: var(--color-card); border: 1px solid var(--color-border); border-radius: var(--r-sm);
  padding: var(--space-5) var(--space-6); min-width: 520px;
}
.ejemplo-hoja .document-section:first-child { padding-top: 0; }
@media (max-width: 700px) { .ejemplo-hoja { padding: var(--space-4); min-width: 460px; } }

/* ── El pie de las tarjetas de foco (inicio · "Qué te falta por trabajar") ──
   Cada tarjeta termina con dos piezas: el tramo y núcleo del objetivo, y el botón que lleva a
   planificarlo. Colgaban del flujo normal, así que quedaban pegadas a la izquierda bajo un
   párrafo de cuatro líneas, y cada botón caía a una altura distinta según lo que midiera el
   texto de su tarjeta — tres botones iguales, desalineados, en una fila de tres.
   La tarjeta pasa a columna flexible: `margin-top: auto` en el pie lo empuja al fondo, así que
   los tres botones quedan a la misma altura pase lo que pase con el texto, y el centrado los
   pone bajo el eje de su propia tarjeta.
   Y SE CENTRA LA TARJETA ENTERA, no sólo el pie. La primera versión dejó la prosa a la
   izquierda por la regla de esta hoja: una medida de lectura larga centrada se sigue con
   dificultad, porque cada línea empieza en una x distinta y el ojo tiene que buscar dónde.
   Pero esa regla habla de las medidas de 54 a 62 caracteres del texto corrido. Medido en esta
   tarjeta: 38 caracteres por línea y cuatro líneas. A ese ancho el ojo no pierde el renglón, y
   dejar el párrafo suelto a la izquierda entre un título y un pie centrados no se lee como una
   decisión de lectura — se lee como un descuadre. */
#cobertura .card { display: flex; flex-direction: column; text-align: center; }
/* La cejilla es una fila flex (lleva su guión delante), así que no la centra text-align. */
#cobertura .card > .eyebrow { justify-content: center; }
#cobertura .card > small { margin-top: auto; padding-top: var(--space-3); text-align: center; }
#cobertura .card > .outline { align-self: center; margin-top: var(--space-2); }

/* ── Trabajando: el botón, su panel de avance y la barra de navegación ──
   Diez acciones de este producto llaman a la IA y tardan entre veinte y cincuenta segundos.
   Hasta ahora lo único que se veía en ese minuto era un botón apagado, que desde fuera no se
   distingue de una página colgada. Tres piezas lo arreglan: el botón se mueve, un panel cuenta
   en qué va, y al navegar entre páginas una barra dice que el clic se recibió. */

.btn.is-trabajando, .outline.is-trabajando { cursor: progress; }
.btn.is-trabajando::after, .outline.is-trabajando::after {
  content: '';
  flex: none;
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  animation: trabajo-giro .7s linear infinite;
}
.btn--lg.is-trabajando::after, .outline--lg.is-trabajando::after { width: 16px; height: 16px; }
/* El punto decorativo de --ai le cede el sitio al indicador: dos círculos seguidos en el mismo
   botón se leen como un error de pintado, no como dos cosas distintas. */
.btn--ai.is-trabajando::before, .outline--ai.is-trabajando::before { display: none; }
@keyframes trabajo-giro { to { transform: rotate(360deg); } }

.trabajo {
  display: flex; flex-direction: column; gap: var(--space-2);
  /* Dentro de una fila de botones que envuelve, esto lo baja a su propia línea en vez de
     dejarlo apretado al lado del botón. Fuera de un contenedor flex no hace nada. */
  flex-basis: 100%;
  margin-top: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--r);
  background: var(--color-card);
}
.trabajo__barra { height: 4px; border-radius: var(--r-pill); background: var(--color-border); overflow: hidden; }
.trabajo__barra > span {
  display: block; height: 100%; width: 38%;
  border-radius: inherit;
  background: var(--color-ai);
  animation: trabajo-avance 1.6s ease-in-out infinite;
}
/* Recorre de lado a lado y no se llena de 0 a 100: no sabemos cuánto falta —la petición es una
   sola y sin streaming— y una barra que se llena promete un final que nadie puede calcular. */
@keyframes trabajo-avance { from { transform: translateX(-110%); } to { transform: translateX(330%); } }
.trabajo__texto { margin: 0; font-size: var(--fs-sm); color: var(--color-text-secondary); }

/* La barra de navegación entre páginas. El sitio es HTML multipágina: cada clic del menú deja
   la pantalla quieta hasta que el servidor responde, sin ninguna señal de que se recibió. */
.nav-progreso {
  position: fixed; inset: 0 0 auto 0; z-index: 9999;
  height: 3px; background: transparent; pointer-events: none;
}
/* El avance lo lleva una animación CSS y no el JS moviendo `width`. La diferencia importa: una
   animación arranca sola al insertar el nodo, mientras que el truco de "pintar en el frame
   siguiente" necesita requestAnimationFrame, que NO corre con la pestaña de fondo — y entonces
   la barra se queda en cero justo cuando más falta hace, que es cuando te fuiste a otra
   pestaña a esperar. Es la misma razón por la que la entrada de los avisos es una animación.

   Llega al 80 % y se queda: no hay forma de saber cuánto falta, y una barra que se completa
   sola promete un final que nadie ha calculado. La completa la página nueva al pintarse. */
.nav-progreso > span {
  display: block; height: 100%; width: 100%;
  transform: scaleX(0); transform-origin: 0 50%;
  background: var(--color-ai);
  animation: nav-progreso-avanza 4s cubic-bezier(.2, .8, .3, 1) forwards;
}
@keyframes nav-progreso-avanza {
  from { transform: scaleX(0); }
  60% { transform: scaleX(.55); }
  to { transform: scaleX(.8); }
}

/* Bajo reduce-motion NO se apagan estos indicadores: son la única señal de que el sistema
   sigue vivo, y quitarlos deja exactamente el problema que vinieron a resolver. Se cambia el
   movimiento por un latido de opacidad, que cumple el mismo papel sin desplazar nada. */
@media (prefers-reduced-motion: reduce) {
  .btn.is-trabajando::after, .outline.is-trabajando::after {
    animation: trabajo-latido 1.4s ease-in-out infinite;
    border-top-color: currentColor;
    opacity: .5;
  }
  .trabajo__barra > span { width: 100%; animation: trabajo-latido 1.4s ease-in-out infinite; }
  /* La barra de navegación aparece entera y quieta: sigue diciendo «se recibió tu clic» sin
     nada que se desplace por la pantalla. */
  .nav-progreso > span { transform: none; animation: none; }
  @keyframes trabajo-latido { 50% { opacity: .25; } }
}

/* ── Mis documentos: rejilla de datos ──
   Era una lista de fichas con el título en negrita y, debajo, «Nivel · Tipo · Fecha» apretados
   en una sola línea de doce píxeles. Con veinte planificaciones generadas por el mismo molde
   los títulos se parecen entre sí, y esa línea no daba forma de compararlas: había que abrirlas
   una por una. Ahora es una tabla, con sus columnas y su orden.

   Tabla de verdad y no una rejilla de <div>: los lectores de pantalla anuncian «columna Creado,
   fila 3» y se puede navegar por celdas. Eso lo da <table> y no lo da display:grid. */
.doc-tabla-scroll { overflow-x: auto; margin: 0 calc(var(--space-4) * -1); padding: 0 var(--space-4); }
.doc-tabla { width: 100%; border-collapse: collapse; font-size: 14px; }
.doc-tabla th, .doc-tabla td { text-align: left; padding: 12px 14px; vertical-align: top; }
.doc-tabla thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--color-card);
  border-bottom: 1.5px solid var(--color-border-strong);
  font: 600 var(--fs-xs)/1.3 var(--font);
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--color-text-secondary);
  white-space: nowrap;
}
.doc-tabla tbody tr { border-bottom: 1px solid var(--color-border); }
.doc-tabla tbody tr:last-child { border-bottom: none; }
/* Mismo realce que .table-row, para que las dos formas de listar del producto se sientan una
   sola cosa: sin cebra, con la línea inferior y el filo de color al pasar el puntero. */
.doc-tabla tbody tr:hover { background: var(--color-primary-soft); box-shadow: inset 3px 0 0 var(--color-accent); }
/* Dos líneas como tope. Los títulos los genera el sistema a partir del texto del OA, así que
   son frases largas y de longitud muy dispar: sin tope, una fila mide una línea y la siguiente
   cuatro, y la tabla pierde la retícula que es su única razón de ser. El título completo va en
   el atributo title, así que no se pierde nada. */
.doc-tabla__doc strong {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
  font-size: 14.5px; font-weight: 650; color: var(--color-heading); line-height: 1.4;
  /* Corta por sílabas antes que dejar un hueco enorme al final de la primera línea: estos
     títulos traen palabras largas —«observación», «recolección»— en una columna estrecha. */
  overflow-wrap: break-word; hyphens: auto;
}
/* Apilada en ficha el título encabeza y hay ancho de sobra: no hace falta recortarlo. */
@media (max-width: 860px) {
  .doc-tabla__doc strong { -webkit-line-clamp: none; display: block; }
}
/* Mismo tope de dos líneas que el título, y por el mismo motivo: «Interacción y comprensión del
   entorno · Exploración del entorno natural» ocupa tres líneas en esta columna y estiraba la
   fila entera. Sin esto, las filas medían 117, 71, 94 y 71 píxeles y la retícula se perdía. */
.doc-tabla__detalle {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
  color: var(--color-text-secondary); max-width: 30ch;
  overflow-wrap: break-word;
}
@media (max-width: 860px) {
  .doc-tabla__detalle { -webkit-line-clamp: none; display: block; }
}
.doc-tabla__fecha { display: block; font-variant-numeric: tabular-nums; white-space: nowrap; }
.doc-tabla td small { color: var(--color-text-secondary); font-variant-numeric: tabular-nums; }
.doc-tabla__vacio { color: var(--color-text-secondary); opacity: .75; }
.doc-tabla__acciones { text-align: right; white-space: nowrap; }
.doc-tabla__acciones .outline { padding: 7px 14px; font-size: 13px; min-height: 0; }

/* La cabecera ordenable es un <button> dentro del <th>: se llega con el tabulador y se activa
   con Enter o Espacio sin que haya que reimplementar nada. El aria-sort vive en el <th>. */
.doc-tabla__orden {
  display: inline-flex; align-items: center; gap: 4px;
  background: none; border: none; padding: 0; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit;
}
.doc-tabla__orden:hover { color: var(--color-heading); text-decoration: underline; text-underline-offset: 3px; }
.doc-tabla__orden:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; border-radius: 2px; }
.doc-tabla__orden.is-activa { color: var(--color-primary); }

/* Bajo 860 px la tabla se apila en fichas. El corte es el ancho al que las siete columnas
   dejan de caber sin que el título se parta en cuatro líneas, no un tamaño de dispositivo.
   El rótulo de cada dato sale de data-col: en una ficha apilada, seis valores sin su nombre
   delante no se distinguen unos de otros. */
@media (max-width: 860px) {
  .doc-tabla-scroll { overflow-x: visible; margin: 0; padding: 0; }
  .doc-tabla, .doc-tabla tbody, .doc-tabla tr, .doc-tabla td { display: block; width: auto; }
  .doc-tabla thead { display: none; }
  .doc-tabla tbody tr {
    border: 1px solid var(--color-border); border-radius: var(--r);
    padding: var(--space-3); margin-bottom: var(--space-3);
  }
  .doc-tabla tbody tr:hover { box-shadow: none; }
  .doc-tabla td { padding: 3px 0; }
  .doc-tabla td[data-col]::before {
    content: attr(data-col) ': ';
    color: var(--color-text-secondary); font-size: 12px; font-weight: 600;
  }
  /* El título encabeza la ficha: no necesita rótulo, porque es lo que se está nombrando. */
  .doc-tabla__doc::before { content: none !important; }
  .doc-tabla__doc { margin-bottom: var(--space-2); }
  .doc-tabla__detalle { max-width: none; }
  .doc-tabla__fecha, .doc-tabla td small { display: inline; }
  .doc-tabla__acciones { text-align: left; margin-top: var(--space-3); }
}

/* ── El formulario de planificación, por secciones ──
   Era un panel plano: cuatro campos a la vista y once metidos en un solo acordeón llamado
   «Ajustes de la experiencia». Los once no tenían nada que ver entre sí —el establecimiento,
   el ámbito, la duración y el comentario en la misma bolsa— y quedaban a un clic de distancia
   que nadie da, así que la mitad de lo que da forma al documento no se usaba nunca.

   Ahora son secciones numeradas en una sola columna. No es un asistente de pantallas a
   propósito: Nielsen Norman recomienda el asistente para tareas que se hacen POCAS veces, y
   esta es la que una educadora repite cada semana. Partirla en pasos obligaría a navegar
   cuatro veces para cambiar un campo. Las secciones dan la estructura sin cobrar ese peaje.

   El orden va de lo más amplio a lo más fino —grupo, objetivo, forma, apoyos—, que es el orden
   en que se decide de verdad y el que usan los configuradores. */

.form-intro {
  margin: 0 0 var(--space-5);
  padding: var(--space-3) var(--space-4);
  border-left: 3px solid var(--color-primary-mid);
  background: var(--color-primary-soft);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: var(--fs-sm); line-height: 1.55; color: var(--color-text);
}
/* Decir de entrada qué cuesta la tarea es de las cuatro reglas de NN/g contra la carga
   cognitiva: quien no sabe cuánto le van a pedir, sobreestima y abandona antes de empezar. */
.form-intro strong { color: var(--color-heading); font-weight: 650; }

.form-seccion { margin-bottom: var(--space-6); }
.form-seccion:last-of-type { margin-bottom: 0; }
.form-seccion__cab {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding-bottom: var(--space-3); margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-border);
}
.form-seccion__cab > div { flex: 1; min-width: 0; }
.form-seccion__cab h2 { margin: 0; font: var(--w-title) var(--fs-lg)/1.25 var(--serif); color: var(--color-heading); }
.form-seccion__sub { margin: 4px 0 0; font-size: var(--fs-sm); color: var(--color-text-secondary); line-height: 1.5; }
.form-seccion__num {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--color-primary); color: var(--color-on-accent);
  font: 650 13px/1 var(--font); font-variant-numeric: tabular-nums;
}
/* Obligatorio y opcional se dicen con palabras y no sólo con un asterisco: el asterisco hay
   que saber leerlo, y aquí la mitad de los campos son opcionales de verdad —los propone la
   IA—, que es justo lo que hay que comunicar para que nadie los rellene por si acaso. */
.form-seccion__marca {
  flex: none; align-self: center;
  padding: 3px 10px; border-radius: var(--r-pill);
  font: 600 var(--fs-3xs)/1.4 var(--font); letter-spacing: .04em; text-transform: uppercase;
  background: var(--color-primary-soft); color: var(--color-primary-deep);
}
.form-seccion__marca--opt { background: var(--color-border); color: var(--color-text-secondary); }
.form-seccion__cuerpo { display: flex; flex-direction: column; gap: var(--space-5); }
.form-seccion__cuerpo > .grid-2 { gap: var(--space-4); }

/* El rótulo y su marca van DENTRO de un mismo elemento, y esto no es un envoltorio de más.
   `.field` es una columna flex, así que el texto suelto del rótulo y el <span> de la marca
   contaban como dos elementos flex y la marca caía a una línea propia: «Espacio» arriba y
   «opcional» debajo, ocupando el ancho entero. Se veía sobre todo junto a «Duración», que no
   la lleva, porque descuadraba las dos columnas de la fila. */
.field__rot { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }

/* La marca de «opcional» pegada al rótulo, no al final de la línea: se lee al mismo tiempo que
   el nombre del campo, que es cuando sirve para decidir si saltárselo. */
.field__opt {
  font: 500 var(--fs-3xs)/1 var(--font); text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-text-secondary);
}

/* ── Opciones en botones, no en desplegable ──
   Cuatro opciones cada uno. Un desplegable con cuatro obliga a abrirlo sólo para saber qué
   hay dentro, y aquí lo de dentro ES la decisión: qué forma tendrá la planificación. Es la
   recomendación de Baymard para listas cortas. */
.opciones-field { border: none; margin: 0; padding: 0; min-width: 0; }
.opciones-field > legend {
  padding: 0; margin-bottom: var(--space-2);
  font: 600 var(--fs-sm)/1.3 var(--font); color: var(--color-heading);
}
.opciones { display: grid; gap: var(--space-2); grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.opcion { position: relative; display: flex; }
.opcion input { position: absolute; opacity: 0; width: 0; height: 0; }
.opcion span {
  flex: 1;
  display: flex; align-items: center; justify-content: center; text-align: center;
  min-height: 44px; padding: 10px 14px;
  border: 1.5px solid var(--color-border-strong); border-radius: var(--r-sm);
  background: var(--color-surface);
  font: 500 14px/1.3 var(--font); color: var(--color-text);
  cursor: pointer;
  transition: border-color var(--ease), background var(--ease), color var(--ease);
}
.opcion span:hover { border-color: var(--color-primary-mid); background: var(--color-primary-soft); }
.opcion input:checked + span {
  border-color: var(--color-primary); background: var(--color-primary-soft);
  color: var(--color-primary-deep); font-weight: 650;
}
/* El foco va en el rótulo porque el radio real es invisible: sin esto, quien navega con
   teclado recorre las cuatro opciones sin ver dónde está. */
.opcion input:focus-visible + span { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.check-linea { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-sm); cursor: pointer; color: var(--color-text); transition: color var(--ease); }
.check-linea:hover { color: var(--color-heading); }

/* La sección plegable no lleva cabecera propia: su número y su título viven en el resumen del
   acordeón, que es la superficie que se pulsa. Dos cabeceras seguidas se leerían como dos
   secciones distintas. */
.form-seccion--plegable > .ajustes { margin: 0; }
.form-seccion--plegable .ajustes > summary { flex-direction: row; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.form-seccion--plegable .ajustes-title { font-size: var(--fs-md); }
.form-seccion--plegable .ajustes-hint { flex-basis: 100%; }

@media (max-width: 700px) {
  .form-seccion__cab { flex-wrap: wrap; }
  .form-seccion__marca { order: -1; margin-left: auto; }
  .form-seccion__cuerpo > .grid-2 { grid-template-columns: 1fr; }
  .opciones { grid-template-columns: 1fr 1fr; }
}

/* ── El asistente de planificación ──
   El formulario entero se muda a un <dialog> y se enseña de fase en fase. La página se queda
   con un lanzador y una tarjeta que resume lo respondido, para que cerrar el pop-up a medias no
   se sienta como haberlo perdido.

   Todo esto vive bajo #form-panel.con-asistente o dentro de .dialog--asistente: sin soporte de
   <dialog> el JS no pone ninguna de las dos y la página se pinta como el formulario abierto de
   siempre, sin una sola regla de aquí interfiriendo. */

/* El formulario sigue en el DOM mientras el asistente está montado —es lo que se muda— pero
   sin él a la vista no habría lanzador que ver. */
#form-panel.con-asistente > #plan-form { display: none; }

/* Toda la columna tiene la misma medida: el título, la tarjeta, el botón y la nota. Antes la
   tarjeta se estiraba al ancho del panel y la entradilla se cortaba a 46 caracteres, así que
   una frase de doce palabras se partía en dos mientras la fila de al lado medía mil píxeles y
   dejaba «editar» a un palmo del valor que edita. */
.lanzador { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--space-5); max-width: var(--measure); margin-inline: auto; }
.lanzador__intro { margin: 0; font: var(--w-title) var(--fs-xl)/1.3 var(--serif); color: var(--color-heading); text-wrap: balance; }
.lanzador__coste { margin: 0; max-width: var(--measure-narrow); }
/* El resumen se queda alineado a la izquierda por dentro: son filas de datos comparables entre
   sí, y centrarlas rompe la columna que permite compararlas de un vistazo. */
.lanzador__resumen { text-align: left; }

/* El saldo y el desplegable del ejemplo se centran en vez de cruzar el panel entero, que es lo
   que hacían antes: el ejemplo medía mil ochocientos píxeles mientras el texto de debajo medía
   seiscientos y quedaba arrimado a la izquierda, y dos tercios de caja en blanco a la derecha
   se leen como «esto está a medio hacer».
   Va acotado a .con-asistente porque .ejemplo lo comparten cinco herramientas. */
#form-panel.con-asistente > #credits-display { max-width: var(--measure); margin-inline: auto; }
/* PERO EL EJEMPLO NO ES PROSA. Centrarlo estaba bien; darle 62ch —la medida de un párrafo—
   no: dentro va una hoja de documento con una tabla de cuatro columnas, y a 535px la hoja
   chocaba contra su propio `min-width: 520px` y sacaba barra de scroll horizontal en una
   pantalla de mil novecientos. La tabla quedaba en 466px y «Nivel Medio · Nivel Medio Mayor ·
   3 a 4 años» se partía en tres renglones.
   Su medida es la de un documento, no la de un texto: cabe la tabla sin romperse, y sigue
   centrado y por debajo del ancho del panel, que era el punto. */
#form-panel.con-asistente > .ejemplo { max-width: min(860px, 100%); margin-inline: auto; }
#form-panel.con-asistente > #credits-display { text-align: center; }
.lanzador__resumen {
  align-self: stretch;
  border: 1px solid var(--color-border);
  border-radius: var(--r);
  background: var(--color-card);
  padding: var(--space-4) var(--space-5);
}
/* La tarjeta necesita decir qué es. Sin cabecera, sus filas se leían como una continuación de
   la entradilla y no como el estado de algo que ya empezaste. */
.lanzador__resumen-cab {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}
.lanzador__resumen-cab h2 { margin: 0; font: 650 var(--fs-md)/1.3 var(--font); color: var(--color-heading); }
/* Cuánto queda, en números. Las filas dicen QUÉ falta; esto dice cuánto, que es lo que decide
   si te pones ahora o lo dejas para luego. */
.lanzador__progreso {
  margin: 0; font-variant-numeric: tabular-nums;
  font: 600 var(--fs-xs)/1.4 var(--font); letter-spacing: .03em; text-transform: uppercase;
  color: var(--color-text-secondary);
}
.lanzador__progreso.is-ready { color: var(--color-primary); }
/* Cuándo se guardó y dónde. «En este dispositivo» no es un detalle técnico de relleno: el
   borrador NO viaja a la base, así que quien planifique desde otro computador no lo va a
   encontrar allí, y es mejor decirlo que dejar que lo descubra buscándolo. */
.lanzador__guardado { margin: var(--space-3) 0 0; }
/* «Empezar de nuevo» al lado y en secundario: destruye trabajo, así que no puede competir en
   peso con la acción que lo continúa. */
.lanzador__acciones { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

/* ── Las filas del resumen ──
   Mismo dibujo en la tarjeta de la página y en la fase de repaso: es el mismo dato, y verlo
   distinto en cada sitio haría dudar de cuál manda. */
.resumen__fila {
  display: grid; grid-template-columns: 6.5rem 1fr auto; align-items: baseline;
  gap: var(--space-3);
  padding: 9px 0;
  border-bottom: 1px solid var(--color-border);
  font-size: var(--fs-sm);
}
.resumen__fila:last-child { border-bottom: none; }
.resumen__rotulo { color: var(--color-text-secondary); font-weight: 600; }
.resumen__valor { color: var(--color-text); min-width: 0; overflow-wrap: break-word; }
.resumen__vacio { color: var(--color-text-secondary); font-style: italic; }
/* Lo que falta y es obligatorio se marca; lo que falta y lo propone la IA, no. Pintar los dos
   igual convertiría en deuda algo que es una comodidad. */
.resumen__fila.is-falta .resumen__vacio { color: var(--color-danger); font-style: normal; font-weight: 600; }
.resumen__editar {
  background: none; border: none; padding: 0; cursor: pointer;
  font: 600 var(--fs-sm)/1 var(--font); color: var(--color-primary);
  text-decoration: underline; text-underline-offset: 3px;
}
.resumen__editar:hover { color: var(--color-primary-deep); }
.resumen__editar:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; border-radius: 2px; }

/* ── El diálogo ── */
.dialog--asistente { width: min(720px, calc(100vw - 2 * var(--space-5))); max-width: none; }
.dialog--asistente .dialog__head { padding-bottom: var(--space-4); border-bottom: 1px solid var(--color-border); }
.dialog--asistente .dialog__head h2 { font-size: var(--fs-lg); }
.dialog--asistente .dialog__body { max-height: min(62vh, 640px); }
/* Dentro del asistente cada fase ES la pantalla: no necesita repetir su propio número ni su
   marca de obligatorio, que ya están en el indicador de arriba y en el pie. */
.dialog--asistente .form-seccion { margin-bottom: 0; }
.dialog--asistente .form-seccion__num,
.dialog--asistente .form-seccion__marca,
.dialog--asistente .form-intro { display: none; }
.dialog--asistente .form-seccion__cab { border-bottom: none; padding-bottom: 0; margin-bottom: var(--space-4); }

/* ── El indicador de fases ── */
.asistente-pasos {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  list-style: none; margin: var(--space-3) 0 0; padding: 0;
}
.paso {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: var(--r-pill);
  border: 1.5px solid var(--color-border);
  background: var(--color-surface);
  font: 500 var(--fs-xs)/1.2 var(--font); color: var(--color-text-secondary);
  cursor: pointer;
  transition: border-color var(--ease), background var(--ease), color var(--ease);
}
.paso__num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--color-border); color: var(--color-text);
  font: 650 11px/1 var(--font); font-variant-numeric: tabular-nums;
}
.paso:hover:not(:disabled) { border-color: var(--color-primary-mid); color: var(--color-heading); }
.paso:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
/* Deshabilitado y no oculto: que se vea cuántas fases quedan es la mitad de para qué sirve un
   indicador. Lo que no se puede es saltar a una que aún no se ha visto. */
.paso:disabled { cursor: default; opacity: .55; }
.paso--actual { border-color: var(--color-primary); background: var(--color-primary-soft); color: var(--color-primary-deep); font-weight: 650; }
.paso--actual .paso__num { background: var(--color-primary); color: var(--color-on-accent); }
.paso--hecha { border-color: var(--color-primary-mid); color: var(--color-heading); }
.paso--hecha .paso__num { background: var(--color-primary); color: var(--color-on-accent); }
.paso--incompleta { border-color: var(--color-danger-border); color: var(--color-danger-ink); }
/* El texto sobre el rojo va por token y no en literal: en tema claro el rojo es oscuro y pide
   blanco, y en oscuro es claro y pide negro. Un #fff fijo aquí desaparecía en tema oscuro. */
.paso--incompleta .paso__num { background: var(--color-danger); color: var(--color-on-accent); }

/* En pantallas estrechas el rótulo de cada fase sobra: seis textos completos en dos líneas
   ocupan más que el propio formulario. Quedan los números, que es lo que orienta. */
@media (max-width: 620px) {
  .paso__txt { display: none; }
  .paso--actual .paso__txt { display: inline; }
}

/* ── El pie ── */
.asistente-pie { display: flex; flex-direction: column; gap: var(--space-2); width: 100%; }
.asistente-pie__aviso { margin: 0; min-height: 1.2em; font-size: var(--fs-sm); color: var(--color-danger); }
.asistente-pie__botones { display: flex; gap: var(--space-3); justify-content: flex-end; flex-wrap: wrap; }
/* Atrás a la izquierda del todo: la salida se lee antes que la acción, igual que en el pie de
   los demás diálogos de este producto. */
.asistente-pie__botones [data-atras] { margin-right: auto; }

@media (max-width: 700px) {
  /* A pantalla completa: un formulario de cinco fases dentro de una tarjeta flotante con
     márgenes a los lados desperdicia el ancho que más falta hace. */
  .dialog--asistente { width: 100vw; max-height: 100vh; height: 100vh; border-radius: 0; }
  /* El `flex:1` de abajo no hacía nada porque el <dialog> es `display:block`: el cuerpo crecía
     hasta el alto del contenido, el que desplazaba era el diálogo entero y el pie con «Atrás /
     Continuar» quedaba a 1300 px de scroll, fuera de la pantalla del teléfono. Con la columna
     flex, el cuerpo es el único que se desplaza y cabecera y pie se quedan fijos.
     Va sobre `[open]`: sin ese filtro este display le ganaría al `display:none` que el
     navegador aplica a los diálogos cerrados, y se verían todos. */
  .dialog--asistente[open] { display: flex; flex-direction: column; }
  .dialog--asistente .dialog__body { max-height: none; flex: 1; }
  .asistente-pie__botones [data-atras] { margin-right: 0; }
  .asistente-pie__botones > * { flex: 1; }
  .resumen__fila { grid-template-columns: 1fr auto; }
  .resumen__rotulo { grid-column: 1 / -1; }
}

/* ── El fondo del lanzador ──
   Esta herramienta es lisa a propósito: la textura de grano vive en `html:not(.is-app)` y se
   quitó de la app con un motivo escrito al lado —«es donde hacen falta las horas de lectura»—.
   Esto NO la devuelve. Se queda en el lanzador, que no es una pantalla de lectura: se llega,
   se pulsa y se entra al diálogo. En cuanto hay un formulario delante, no existe.

   Papel de cuaderno, que es el lenguaje que el producto ya habla —cinta adhesiva sobre las
   tarjetas, una hoja asomando detrás de la vista previa— y no dibujos de párvulos: quien usa
   esto es una educadora trabajando, y un fondo infantil la trataría como destinataria del
   material en vez de como su autora.

   Los colores salen de --color-primary-rgb, que existe justamente para los alfas y está
   declarado en los dos temas: el rayado sigue al tema oscuro sin una segunda regla. */
#form-panel.con-asistente { position: relative; }
#form-panel.con-asistente::before {
  content: '';
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(38rem 24rem at 12% 16%, rgb(var(--color-primary-rgb) / .09), transparent 70%),
    radial-gradient(32rem 22rem at 88% 84%, rgb(var(--color-primary-rgb) / .06), transparent 70%),
    repeating-linear-gradient(to bottom, transparent 0 39px, rgb(var(--color-primary-rgb) / .05) 39px 40px);
  background-position: 0 0, 0 0, 0 0;
  background-repeat: no-repeat, no-repeat, repeat;
}
/* El contenido va POR ENCIMA. Sin esto el ::before se pinta sobre el panel y el rayado cruza
   por delante del texto en vez de por detrás. */
#form-panel.con-asistente > * { position: relative; z-index: 1; }

/* Lo «dinámico» son las dos brumas, y se mueven MUY despacio: treinta y cuatro segundos de ida
   y otros tantos de vuelta. Un fondo que se nota moviéndose deja de ser fondo, y esta pantalla
   se abre varias veces al día. El rayado no se mueve: unas líneas que se desplazan detrás de
   un texto marean. */
@media (prefers-reduced-motion: no-preference) {
  #form-panel.con-asistente::before { animation: lanzador-bruma 34s ease-in-out infinite alternate; }
  @keyframes lanzador-bruma { to { background-position: 7% 5%, -6% -4%, 0 0; } }
}
