/* Tipografía autoalojada: la web presume de no llamar a terceros,
   así que tampoco llama a Google Fonts. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('assets/fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('assets/fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Display: sólo para H1, marca y H2 de sección (ver tools/NOTAS.md, pedido
   2026-09-09 tras el "se ve muy genérico" — el cuerpo y la UI de las
   herramientas se quedan en Inter, esto es sólo para lo grande). También
   autoalojada por la misma razón que Inter arriba. Variable, con eje óptico
   (opsz): a tamaño de titular se activa su corte de exhibición solo, sin
   CSS aparte. */
@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('assets/fonts/bricolage-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('assets/fonts/bricolage-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==========================================================================
   Herramenta — hoja de estilo única
   Orden: tokens · base · controles · header/footer · tool card · secciones · ads
   ========================================================================== */

/* ── 1. Tokens ─────────────────────────────────────────────────────────── */
:root {
  /* Repaleta 2026-09-06: neutros de beige cálido a gris frío, y acento de
     naranja a índigo — decisión del dueño tras comparar las dos direcciones
     en vivo (ver tools/NOTAS.md). Contraste recalculado desde cero, no
     heredado del naranja anterior. */
  --ink:        #14151a;  --ink:        light-dark(#14151a, #eef0f3);
  --ink-soft:   #494c56;  --ink-soft:   light-dark(#494c56, #aab0bb);
  /* 4,66:1 sobre --surface-2 en claro; 5,14:1 en oscuro. */
  --ink-faint:  #686b74;  --ink-faint:  light-dark(#686b74, #8b8f99);
  --paper:      #f7f8fb;  --paper:      light-dark(#f7f8fb, #0d0e12);
  --surface:    #ffffff;  --surface:    light-dark(#ffffff, #16171d);
  --surface-2:  #eef0f3;  --surface-2:  light-dark(#eef0f3, #1c1e25);
  --line:       #dde1e7;  --line:       light-dark(#dde1e7, #2b2e37);
  --line-soft:  #e7eaef;  --line-soft:  light-dark(#e7eaef, #23252d);

  --accent:     #4338ca;  --accent:     light-dark(#4338ca, #818cf8);
  --accent-ink: #ffffff;  --accent-ink: light-dark(#ffffff, #141033);
  --accent-soft:#ebeafc;  --accent-soft:light-dark(#ebeafc, #1c1a35);
  /* A diferencia del naranja, el índigo SÍ vale como texto directo: 6,92:1
     sobre --surface-2 (el peor caso), muy por encima del mínimo de 4,5. No
     hace falta un segundo tono — accent y accent-text son el mismo. */
  --accent-text:#4338ca;  --accent-text:light-dark(#4338ca, #818cf8);

  --ok:         #0f7b52;  --ok:         light-dark(#0f7b52, #3ec98d);
  --warn:       #a15c00;  --warn:       light-dark(#a15c00, #e0a03a);
  --warn-soft:  #fdf5e3;  --warn-soft:  light-dark(#fdf5e3, #2b2314);
  --danger:     #b4231d;  --danger:     light-dark(#b4231d, #f0736c);
  --danger-soft:#fdeceb;  --danger-soft:light-dark(#fdeceb, #2e1717);

  --r-sm: 8px;
  --r:    14px;
  --r-lg: 22px;

  /* light-dark() sólo acepta colores, no una lista de box-shadow entera,
     así que lo que cambia de tema es el color de cada capa. En oscuro la
     segunda capa de --shadow-sm se apaga: sobre fondo negro un halo ancho
     se ve como suciedad, no como elevación. */
  --shadow-sm-1: light-dark(rgba(22,21,26,.06), rgba(0,0,0,.4));
  --shadow-sm-2: light-dark(rgba(22,21,26,.04), transparent);
  --shadow-1:    light-dark(rgba(22,21,26,.05), rgba(0,0,0,.35));
  --shadow-2:    light-dark(rgba(22,21,26,.11), rgba(0,0,0,.5));
  --shadow-sm: 0 1px 2px var(--shadow-sm-1), 0 2px 8px var(--shadow-sm-2);
  /* Repensada 2026-09-08 (32px→18px) y otra vez 2026-09-09 (18px→14px, más
     opacidad para compensar): el detector de impeccable seguía marcando
     "gpt-thin-border-wide-shadow" con 18px. Menos difusión todavía, más
     resuelto — una sombra que parece dibujada, no un halo genérico. */
  --shadow:    0 1px 2px var(--shadow-1), 0 6px 14px var(--shadow-2);

  --wrap: 1120px;
  --sans: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --display: "Bricolage Grotesque", var(--sans);

  /* light-dark() necesita saber en qué esquema está. Sin fijar nada sigue
     al sistema; el botón de tema (main.js, initTema) pone data-theme. */
  color-scheme: light dark;
}

:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }


/* ── 2. Base ───────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

/* Bug real encontrado en el pase de diseño de 2026-09-13, no cosmético: la
   hoja de estilos fija `display` (block/flex/grid/inline-flex...) en varias
   clases que a la vez se usan sobre elementos que JS oculta con la propiedad
   `hidden` (.notice, .detalle-row, .lista-img, .btn, .firma-preview...). La
   cascada da a CUALQUIER regla de autor —por floja que sea su especificidad—
   prioridad sobre la hoja del navegador, así que ese `display` ganaba
   siempre a `[hidden]{display:none}` y el elemento se veía aunque JS lo
   hubiera marcado oculto. Encontrado en vivo: con un sueldo de 2.143 €/mes
   (muy por debajo del tope de cotización) `calculadora-sueldo-neto.html`
   seguía mostrando «Has superado la base máxima de cotización» porque el
   aviso es un `.notice` y `.notice{display:block}` tapaba su `hidden`.
   Mismo defecto en `#hp-comision-row` (.detalle-row), `#meta-lista`
   (.lista-img), `#meta-descargar-todas` (.btn) y `#firma-preview`
   (.firma-preview) — la única excepción que ya funcionaba por accidente era
   `aside.ad-toast[hidden]`, que años atrás se blindó igual que aquí. Una
   regla, `!important`, arriba de todo: gana a cualquier `display` que se
   añada después sin tener que acordarse de repetir el blindaje clase a
   clase. La regla de `aside.ad-toast` queda ahora redundante pero no
   estorba; no se ha tocado para no mezclar dos cambios en el mismo sitio. */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { line-height: 1.18; margin: 0 0 .5em; letter-spacing: -.02em; font-weight: 700; }
h1 { font-family: var(--display); font-weight: 800; letter-spacing: -.03em; font-size: clamp(1.75rem, 1.15rem + 2.4vw, 2.85rem); }
h2 { font-size: clamp(1.35rem, 1.05rem + 1.3vw, 1.95rem); }
h3 { font-size: 1.1rem; }
/* Título de sección (no las etiquetas pequeñas de panel ni el <h2> del
   diálogo de anuncio: esos son roles de UI, no titulares). */
.section > .wrap > h2 { font-family: var(--display); font-weight: 700; letter-spacing: -.03em; }
p  { margin: 0 0 1em; }
a  { color: var(--accent-text); text-decoration-thickness: 1px; text-underline-offset: 3px; }
img, canvas, svg { max-width: 100%; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* Lo que dibuja el navegador y no habíamos vestido: selección, cursor de
   texto y barras de desplazamiento. Salen de los mismos tokens, así que
   siguen al tema claro/oscuro sin reglas aparte. */
::selection { background: var(--accent-soft); color: var(--ink); }
input, textarea { caret-color: var(--accent); }
/* El flash gris por defecto al tocar un enlace en móvil tampoco es nuestro:
   mismo criterio que la selección de texto y el cursor de arriba. */
html { -webkit-tap-highlight-color: color-mix(in srgb, var(--accent) 22%, transparent); }
html { scrollbar-color: var(--line) var(--surface-2); scrollbar-width: thin; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--surface-2); }
::-webkit-scrollbar-thumb {
  background: var(--line); border-radius: 99px; border: 3px solid var(--surface-2);
}
::-webkit-scrollbar-thumb:hover { background: var(--ink-faint); }

.wrap { width: min(100% - 2.5rem, var(--wrap)); margin-inline: auto; }
.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;
}
/* Oculta como .sr-only hasta que recibe foco de teclado (Tab): entonces se
   planta arriba a la izquierda, visible, para saltar los 7 enlaces de nav
   antes de llegar al contenido. Sin esto, cada página exige tabular por toda
   la cabecera para llegar a lo que trajo a la persona aquí. */
.skip-link {
  position: absolute; top: -3rem; left: .75rem; z-index: 100;
  background: var(--accent); color: var(--accent-ink); padding: .6rem 1.1rem;
  border-radius: var(--r-sm); font-weight: 600; text-decoration: none;
  transition: top .15s ease;
}
.skip-link:focus { top: .75rem; }
@media (prefers-reduced-motion: reduce) { .skip-link { transition: none; } }

/* ── 3. Controles ──────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  font: inherit; font-weight: 600; line-height: 1.3;
  padding: .8rem 1.2rem; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  cursor: pointer; text-decoration: none;
  transition: background .15s ease, border-color .15s ease, transform .1s ease;
}
.btn:hover { border-color: var(--ink-faint); }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .45; cursor: not-allowed; }
.btn--primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn--primary:hover { filter: brightness(1.06); border-color: var(--accent); }
.btn--big { padding: 1.05rem 1.6rem; font-size: 1.05rem; border-radius: var(--r); }
.btn--link { background: none; border: none; padding: .4rem 0; color: var(--accent-text); text-decoration: underline; cursor: pointer; }

.field { display: block; margin-bottom: 1rem; }
.field > .label {
  display: block; font-size: .82rem; font-weight: 600; letter-spacing: .01em;
  color: var(--ink-soft); margin-bottom: .35rem;
}
.field .hint { display: block; font-size: .78rem; color: var(--ink-faint); margin-top: .3rem; }

/* Avisos de validación (main.js, validarCampo). Dos niveles a propósito:
   "error" es que no se ha entendido lo escrito, "ajuste" es que sí se ha
   entendido pero se ha topado con un límite. Pintar los dos de rojo haría
   que tocar el tope pareciera un fallo del usuario. */
.campo-aviso {
  display: flex; gap: .35rem; align-items: baseline;
  font-size: .78rem; font-weight: 500; margin-top: .35rem;
}
.campo-aviso::before { font-weight: 700; }
.campo-aviso[data-tipo="error"] { color: var(--danger); }
.campo-aviso[data-tipo="error"]::before { content: "!"; }
.campo-aviso[data-tipo="ajuste"] { color: var(--ink-soft); }
.campo-aviso[data-tipo="ajuste"]::before { content: "→"; }

input.is-invalido { border-color: var(--danger); }
input.is-invalido:focus { outline-color: var(--danger); border-color: var(--danger); }
/* El ajuste no tiñe el borde: el dato es válido, sólo se ha recortado. */
input.is-ajustado { border-color: var(--ink-faint); }

input[type="text"], input[type="url"], input[type="password"], input[type="number"], select, textarea {
  width: 100%; font: inherit; color: var(--ink);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: .7rem .85rem;
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }

/* El salto de contenido y el botón de volver arriba mueven el foco a <main>.
   Es un destino, no un control: el anillo ahí sólo desconcierta. */
[tabindex="-1"]:focus, [tabindex="-1"]:focus-visible { outline: none; }

/* Alto contraste de Windows: los colores de marca se descartan y el foco de
   acento desaparece. CanvasText es el único color garantizado. */
@media (forced-colors: active) {
  :focus-visible { outline: 2px solid CanvasText; outline-offset: 2px; }
  .btn, .arriba, input, select, textarea { border: 1px solid CanvasText; }
  input.is-invalido { border: 2px solid CanvasText; }
}
input[type="range"] { width: 100%; accent-color: var(--accent); }
input[type="color"] {
  width: 100%; height: 44px; padding: 3px; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm);
}
input[type="file"] { font-size: .85rem; }

/* Selector de color + su hex escribible al lado: el `<input type="color">`
   a solas obliga a acertar un tono a base de arrastrar el cursor por una
   rueda — quien busca su color de marca exacto ("#E63946") lo escribe una
   vez y ya. El propio swatch se encoge a 44px (mismo alto que .icon-btn) en
   vez de ocupar toda la fila; el texto se lleva el resto. */
.color-input { display: flex; gap: .5rem; }
.color-input input[type="color"] { width: 44px; flex: none; }
.color-input input.hex { flex: 1; min-width: 0; text-transform: uppercase; }
.color-pair { display: flex; align-items: flex-end; gap: .6rem; }
.color-pair .field { flex: 1; min-width: 0; }
.color-swap {
  flex: none; margin-bottom: 1px; border: 1px solid var(--line);
  border-radius: var(--r-sm); color: var(--ink-soft);
}
.color-swap:hover { color: var(--ink); border-color: var(--ink-faint); }
.color-swap svg { width: 18px; height: 18px; transition: transform .2s ease; }
.color-swap:active svg { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .color-swap:active svg { transform: none; } }

.segmented { display: flex; flex-wrap: wrap; gap: .4rem; }
.segmented input { position: absolute; opacity: 0; pointer-events: none; }
.segmented label {
  flex: 1 1 auto; min-width: max-content; text-align: center;
  padding: .55rem .8rem; font-size: .88rem; font-weight: 600;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface); color: var(--ink-soft); cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease, transform .1s ease;
}
.segmented label:hover { border-color: var(--ink-faint); }
.segmented label:active { transform: scale(.96); }
.segmented input:checked + label { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
@media (prefers-reduced-motion: reduce) { .segmented label:active { transform: none; } }
.segmented input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── 4. Header / footer ────────────────────────────────────────────────── */
.site-header {
  border-bottom: 1px solid var(--line-soft);
  background: var(--paper);
  position: sticky; top: 0; z-index: 40;
}
.site-header .wrap { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: .8rem 0; }
.brand { display: inline-flex; align-items: center; gap: .55rem; font-family: var(--display); font-weight: 800; font-size: 1.2rem; color: var(--ink); text-decoration: none; letter-spacing: -.02em; }
.brand svg { width: 26px; height: 26px; color: var(--accent); }
.site-header nav { display: flex; gap: 1.2rem; font-size: .92rem; font-weight: 600; }
.site-header nav a { color: var(--ink-soft); text-decoration: none; }
.site-header nav a:hover { color: var(--accent-text); }
.site-header nav a[aria-current="page"] { color: var(--accent-text); }

/* Menú plegable (main.js, initMenu). Las siete herramientas no caben en una
   línea por debajo de ~920 px: en vez de una tira que se corta, un botón
   «Menú» que despliega la lista entera bajo la cabecera. Sin JavaScript
   (html.no-js) se queda la tira desplazable de siempre. */
.menu-btn {
  display: none; align-items: center; gap: .45rem; margin-left: auto;
  min-height: 40px; padding: 0 .85rem; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--ink); font: inherit; font-size: .9rem; font-weight: 600;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: background-color .18s ease, border-color .18s ease, transform .18s ease;
}
.menu-btn svg { width: 18px; height: 18px; }
.menu-btn .menu-btn__cerrar { display: none; }
.menu-btn[aria-expanded="true"] .menu-btn__abrir { display: none; }
.menu-btn[aria-expanded="true"] .menu-btn__cerrar { display: block; }
.menu-btn:active { transform: scale(.96); transition-duration: .09s; }
.menu-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (hover: hover) and (pointer: fine) { .menu-btn:hover { border-color: var(--accent); } }

@media (max-width: 920px) {
  html:not(.no-js) .menu-btn { display: inline-flex; }
  html:not(.no-js) .site-header .tema-btn { margin-left: 0; }
  html:not(.no-js) .site-header nav.menu {
    display: none; position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; gap: 0; padding: .4rem max(1.25rem, (100% - var(--wrap)) / 2) 1rem;
    background: var(--paper); border-bottom: 1px solid var(--line);
    box-shadow: 0 18px 40px rgba(15, 14, 20, .12);
    font-size: 1rem; overflow: visible; -webkit-mask-image: none; mask-image: none;
    max-height: calc(100dvh - 100%); overflow-y: auto;
  }
  html:not(.no-js) .site-header.is-menu-abierto nav.menu { display: flex; animation: menu-in .18s cubic-bezier(.22,.61,.36,1); }
  html:not(.no-js) .site-header nav.menu a {
    display: flex; align-items: center; min-height: 48px; padding: .2rem 0;
    border-bottom: 1px solid var(--line-soft); color: var(--ink); white-space: normal;
  }
  html:not(.no-js) .site-header nav.menu a:last-child { border-bottom: 0; }
  html:not(.no-js) .site-header nav.menu a[aria-current="page"] { color: var(--accent-text); }
  html:not(.no-js) .site-header nav.menu a[aria-current="page"]::after {
    content: "Estás aquí"; margin-left: auto; font-size: .78rem; font-weight: 600; color: var(--ink-faint);
  }
}
@keyframes menu-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .site-header.is-menu-abierto nav.menu { animation: none !important; } }
@media print { .menu-btn { display: none !important; } }

/* Botón de tema (main.js, initTema). Va suelto en .wrap, no dentro del
   <nav>: en móvil el nav se desplaza en horizontal y el botón se perdería
   con los enlaces. flex:none para que no lo encoja la tira de navegación. */
.tema-btn {
  flex: none; display: grid; place-items: center;
  width: 36px; height: 36px; margin-left: .2rem;
  border-radius: 999px; color: var(--ink-soft);
  -webkit-tap-highlight-color: transparent;
  transition: color .18s ease, background-color .18s ease, transform .18s ease;
}
.tema-btn svg { width: 17px; height: 17px; }
.tema-btn:active { transform: scale(.9); transition-duration: .09s; }
@media (hover: hover) and (pointer: fine) {
  .tema-btn:hover { color: var(--ink); background: var(--surface-2); }
}
@media print { .tema-btn { display: none !important; } }

.site-footer { border-top: 1px solid var(--line-soft); margin-top: 4rem; padding: 2.2rem 0 2.6rem; color: var(--ink-faint); font-size: .88rem; }
.site-footer .wrap { display: grid; gap: 1.4rem; }
.site-footer a { color: var(--ink-soft); text-decoration: none; }
.site-footer a:hover { color: var(--accent-text); }
.site-footer a[aria-current="page"] { color: var(--ink); }
.site-footer .foot-tools { display: flex; flex-wrap: wrap; gap: .5rem 1.4rem; font-weight: 600; }
.site-footer .foot-base {
  display: flex; flex-wrap: wrap; gap: .6rem 1.4rem; align-items: center; justify-content: space-between;
  padding-top: 1.2rem; border-top: 1px solid var(--line-soft);
}
.site-footer .foot-links { display: flex; flex-wrap: wrap; gap: 1.1rem; }

/* ── 5. Cabecera de herramienta ────────────────────────────────────────── */
.tool-head { padding: 2rem 0 1.2rem; text-align: center; }
.tool-head p.lede { font-size: 1.08rem; color: var(--ink-soft); max-width: 62ch; margin-inline: auto; }

/* En móvil la intro se comprime: la herramienta tiene que caber sin scroll. */
@media (max-width: 620px) {
  .tool-head { padding: 1.1rem 0 .8rem; }
  .tool-head h1 { font-size: 1.5rem; }
  .tool-head p.lede { font-size: .95rem; line-height: 1.45; }
  .site-header .wrap { padding: .55rem 0; }
}

/* ── 5b. Portada: hero ─────────────────────────────────────────────────
   Pedido 2026-09-09: la portada leía como plantilla de SaaS — texto
   centrado, tamaño de intro genérico. Modificador propio (no toca
   .tool-head en el resto de páginas, ahí el centrado sigue siendo lo
   correcto para una sola herramienta): titular a la izquierda, a escala de
   verdad, con el halo de --accent detrás en vez de un fondo plano. */
.home-hero {
  display: flex; align-items: center; justify-content: space-between; gap: 2.5rem;
  text-align: left; padding-block: 3.2rem 1.6rem;
}
/* El halo va en la sección entera, no en la caja del titular: pintado en
   .home-hero quedaba recortado por los bordes de la caja y se veía un
   rectángulo. Aquí nace pegado a la cabecera y se desvanece del todo antes
   de llegar a cualquier borde visible. */
.section--hero {
  background-image: radial-gradient(900px 520px at 12% 0%, color-mix(in srgb, var(--accent) 13%, transparent), transparent 72%);
  background-repeat: no-repeat;
}
.home-hero-copy { min-width: 0; flex: 1 1 420px; }
.home-hero h1 {
  max-width: 15ch; font-size: clamp(2.5rem, 1.35rem + 5vw, 4.6rem); line-height: 1.02;
}
.home-hero h1 .accent-em { color: var(--accent-text); }
.home-hero p.lede { margin-inline: 0; text-align: left; max-width: 52ch; font-size: 1.15rem; }

/* Ilustración del hero: cluster de objetos (QR, firma, %, foto sin ubicación,
   candado) generado 2026-09-09 a pedido del usuario para llenar el hueco
   vacío a la derecha del titular en escritorio. Entra con el mismo timing
   que ya usan los .reveal del resto del sitio (no es un efecto nuevo, es el
   mismo lenguaje) y en reposo flota despacio para que la portada no se
   sienta estática. Fuera en tablet/móvil: no hay sitio junto al titular
   grande, y forzarla ahí competiría con la lista de herramientas. */
.home-hero-art {
  width: min(300px, 30vw); height: auto; flex: none; border-radius: var(--r-lg);
  filter: drop-shadow(0 20px 55px color-mix(in srgb, var(--accent) 30%, transparent));
  animation: hero-art-in .7s cubic-bezier(.22,.61,.36,1) both,
             hero-art-float 6s ease-in-out .7s infinite;
}
@keyframes hero-art-in {
  from { opacity: 0; transform: translateY(16px) scale(.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes hero-art-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-8px); }
}
/* Dos ilustraciones según el tema. La de claro viene recortada (fondo
   transparente), así que el radio de esquina no pinta nada y la sombra de
   acento se dibuja alrededor de los objetos, no de un cuadro. La original
   de fondo negro se queda para oscuro, que es donde encaja.
   display:contents para que el <img> siga siendo el hijo flex del hero. */
.home-hero-pic { display: contents; }
.home-hero-art[src*="hero-cluster-claro"] {
  border-radius: 0;
  filter: drop-shadow(0 18px 30px color-mix(in srgb, var(--accent) 22%, transparent));
}
@media (max-width: 900px) { .home-hero-art { display: none; } }
@media (prefers-reduced-motion: reduce) { .home-hero-art { animation: none; } }

@media (max-width: 620px) {
  .home-hero { padding-block: 1.6rem 1rem; }
  .section--hero { background-image: radial-gradient(520px 380px at 0% 0%, color-mix(in srgb, var(--accent) 13%, transparent), transparent 72%); }
  .home-hero h1 { font-size: clamp(2rem, 1.5rem + 3vw, 2.6rem); max-width: 13ch; }
  .home-hero p.lede { font-size: 1rem; }
}

/* ── 5c. Portada: índice de herramientas ───────────────────────────────
   Reemplaza la rejilla de tarjetas idénticas (siete cajas iguales es el
   patrón menos específico que hay) por una lista-índice: mismo lenguaje que
   .rule-list, con numeración en la tipografía de titular en vez de un icono
   en círculo. .cards/.card--link se quedan intactos: los siguen usando el
   bloque "Más herramientas" de cada página de herramienta y el roadmap de
   aquí abajo, que sí son contextos secundarios donde la rejilla es correcta. */
/* Acciones del hero: la portada tenía titular y entradilla y ni un botón.
   El primario va a una herramienta concreta, no a un ancla: quien llega
   buscando "generador de QR" ya sabe lo que quiere. */
.home-hero-acciones { display: flex; flex-wrap: wrap; gap: .7rem; margin-top: 1.6rem; }

/* Cierre de portada: después del texto largo no había ninguna salida. */
.cta-cierre {
  text-align: center; padding: clamp(2.2rem, 5vw, 3.4rem) 1.4rem;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface);
}
.cta-cierre h2 { margin: 0; }
.cta-cierre p { color: var(--ink-soft); margin: .6rem auto 1.6rem; max-width: 46ch; }

/* Volver arriba (main.js, initVolverArriba). Las páginas de herramienta son
   largas: panel de controles arriba, resultados y texto SEO abajo. */
.arriba {
  position: fixed; z-index: 60;
  right: clamp(1rem, 3vw, 1.6rem);
  /* La barra de gestos de iOS se come el borde inferior. */
  bottom: calc(1.1rem + env(safe-area-inset-bottom, 0px));
  display: grid; place-items: center; width: 44px; height: 44px;
  border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .07), 0 12px 28px -14px rgba(0, 0, 0, .3);
  opacity: 0; transform: translateY(10px) scale(.9); pointer-events: none; visibility: hidden;
  transition: opacity .22s ease, transform .22s cubic-bezier(.22,.61,.36,1),
              visibility 0s linear .22s, border-color .18s ease;
}
.arriba.is-visible { opacity: 1; transform: none; pointer-events: auto; visibility: visible; transition-delay: 0s; }
.arriba svg { width: 19px; height: 19px; }
.arriba:active { transform: scale(.92); transition-duration: .1s; }
@media (hover: hover) and (pointer: fine) { .arriba:hover { border-color: var(--ink-faint); } }
@media (prefers-reduced-motion: reduce) { .arriba { transition: opacity .01s, visibility 0s; } }
@media print { .arriba { display: none !important; } }

.tool-index { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); counter-reset: tool; }
.tool-index li { counter-increment: tool; border-bottom: 1px solid var(--line); }
.tool-index a {
  display: grid; grid-template-columns: 2.6rem 1fr auto; align-items: center; gap: 1.1rem;
  padding: 1.35rem .2rem; text-decoration: none; color: inherit;
  transition: transform .2s cubic-bezier(.22,.61,.36,1);
}
.tool-index .idx {
  font-family: var(--display); font-weight: 700; font-size: 1.15rem;
  color: var(--ink-faint); font-variant-numeric: tabular-nums;
  transition: color .2s ease;
}
.tool-index .idx::before { content: counter(tool, decimal-leading-zero); }
.tool-index-body { min-width: 0; }
.tool-index h3 { margin: 0 0 .2rem; font-size: 1.08rem; }
.tool-index p { margin: 0; font-size: .92rem; color: var(--ink-soft); max-width: 58ch; }
.tool-index .arrow { flex: none; width: 20px; height: 20px; color: var(--ink-faint); transition: transform .2s cubic-bezier(.22,.61,.36,1), color .2s ease; }
@media (hover: hover) and (pointer: fine) {
  .tool-index a:hover { transform: translateX(.5rem); }
  .tool-index a:hover .idx, .tool-index a:hover .arrow { color: var(--accent-text); }
  .tool-index a:hover .arrow { transform: translateX(4px); }
}
.tool-index a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: var(--r-sm); }
@media (prefers-reduced-motion: reduce) { .tool-index a, .tool-index .idx, .tool-index .arrow { transition: none; } }
@media (max-width: 620px) {
  .tool-index a { grid-template-columns: 2rem 1fr; padding-block: 1.1rem; }
  .tool-index .idx { font-size: 1rem; }
  .tool-index .arrow { display: none; }
}

/* Variante compacta: el bloque "Más herramientas" al final de cada página de
   herramienta (pedido 2026-09-09 — antes era la misma rejilla de cajas
   iguales que la portada, sólo que sin arreglar). Mismo componente que la
   portada para que "lista de herramientas" sea un único lenguaje en todo el
   sitio; aquí sin párrafo (ya la conoces, sólo hace falta el enlace) y con
   menos aire porque es un bloque secundario al pie, no el protagonista. */
.tool-index--compact a { padding-block: .95rem; }
.tool-index--compact .idx { font-size: 1rem; }
.tool-index--compact h3 { margin: 0; font-size: .98rem; }

/* ── 5d. Rejilla de tipos (numerada) ────────────────────────────────────
   Para listas de categorías cortas que NO son pasos ni enlaces (los ocho
   tipos de código QR, por ejemplo): ni .steps (eso es una secuencia con
   orden) ni .card (el contenedor perezoso por defecto, ya señalado por el
   auditor). La primera versión probó icono en ficha de acento — el propio
   detector de impeccable la marcó como "icon-tile-stack": esa combinación
   (icono en cuadrado + h3, repetida en rejilla) es tan genérica como las
   tarjetas que sustituía. En vez de pedir prestado un motivo ajeno, reusa
   la numeración en la tipografía de titular que ya es la firma propia del
   sitio (mismo recurso que .tool-index) — sin borde, sin fondo, sin icono. */
.type-grid { list-style: none; margin: 0; padding: 0; counter-reset: tipo; display: grid; gap: 1.8rem 1.4rem; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.type-grid > li { counter-increment: tipo; }
.type-grid .idx {
  display: block; font-family: var(--display); font-weight: 700; font-size: 1.25rem;
  color: var(--accent-text); margin-bottom: .3rem; font-variant-numeric: tabular-nums;
}
.type-grid .idx::before { content: counter(tipo, decimal-leading-zero); }
.type-grid h3 { margin: 0 0 .25rem; font-size: 1rem; }
.type-grid p { margin: 0; font-size: .88rem; color: var(--ink-soft); }

/* ── 6. La tarjeta de herramienta ──────────────────────────────────────── */
.tool-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg);
  /* El halo inferior ya lo da --shadow; el filo de luz arriba es lo que
     falta para que lea como una superficie física y no como un <div>
     con sombra de plantilla. Mismo truco en las dos tarjetas de la
     comparativa antes/después de las herramientas de foto. */
  box-shadow: inset 0 1px 0 color-mix(in srgb, white 30%, transparent), var(--shadow);
  padding: clamp(1rem, .6rem + 1.6vw, 1.8rem);
}
.tool-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.6rem; }
@media (min-width: 900px) { .tool-grid { grid-template-columns: minmax(0, 400px) minmax(0, 1fr); gap: 2rem; } }

.panel { min-width: 0; }
.panel > h2 { font-size: .8rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-faint); margin-bottom: .9rem; }

fieldset { border: 0; margin: 0 0 1.1rem; padding: 0; }
fieldset > legend { font-size: .82rem; font-weight: 600; color: var(--ink-soft); padding: 0; margin-bottom: .35rem; }

.row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: .8rem; }

.preview-stack { display: grid; gap: 1rem; }
.preview-box {
  position: relative; display: grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--line-soft);
  border-radius: var(--r); padding: 1.4rem 1rem 1rem; min-height: 240px;
}
/* Antes iba en VERSALES fijas: legible para "Tu código" (2 palabras) pero
   ilegible para "Objeto 3D — gíralo con el ratón" (una instrucción, no una
   etiqueta corta) — mayúsculas sostenidas rompen el reconocimiento de forma
   de palabra en frases largas (encontrado por el auditor de impeccable,
   2026-09-08). El peso, el trazo y el color ya bastan para leerse como
   etiqueta a cualquier longitud, sin ese límite. */
.preview-box .cap {
  position: absolute; top: .85rem; left: 1rem;
  font-size: .74rem; font-weight: 700; letter-spacing: .01em;
  color: var(--ink-faint);
}
/* La vista 3D gira debajo de la etiqueta y puede dejar una cara clara del
   objeto justo detrás: el texto gris pensado para --surface-2 se vuelve
   ilegible. Misma píldora con blur que ya resuelve esto en .ba-tag, para no
   inventar un segundo patrón para el mismo problema. */
.preview-box .cap--chip {
  top: .7rem; left: .8rem; z-index: 2;
  background: rgba(12,11,16,.72); color: #fff;
  padding: .3rem .6rem; border-radius: 999px;
  backdrop-filter: blur(4px);
}

/* El bisel de teléfono en CSS (blanco, con o sin botón de inicio) se quitó
   del todo el 2026-09-08: pedido explícito del usuario tras comparar contra
   sus 4 referencias reales — "banda-arriba" pasó a usar la misma carcasa
   negra que ya dibujaba "banda-abajo" dentro del propio archivo (ver
   frameLayout/applyFrame en qr3d.js), así que un bisel de más en CSS se veía
   como un marco metido dentro de otro marco. `.qr-phone` queda como envoltura
   neutra, sin fondo ni borde propios. */
.qr-phone { display: inline-block; }

/* La caja ya no superpone la etiqueta ("Tu código"/"Imagen") encima del QR
   con .cap--chip: a anchos de panel intermedios el QR queda centrado más
   cerca del borde y la píldora sí llegaba a tapar la esquina real del
   código (encontrado el 2026-09-08). Aquí la etiqueta va en el flujo normal,
   empujando el código hacia abajo — nunca puede superponerse. */
.preview-box--qr { gap: .8rem; }
.preview-box--qr .cap { position: static; }

/* Sin marco ("ninguno") es el código puro pedido el 2026-09-08: nada de
   borde ni recuadro alrededor, sólo tamaño/responsive. Las formas CON marco
   (banda arriba/abajo, ticket) ya traen su propia carcasa negra dibujada
   dentro del canvas/SVG (ver applyFrame/frameSVG), así que este selector
   nunca necesita un borde CSS aparte que sincronizar con la forma activa. */
#qr-preview canvas, #qr-preview svg { display: block; width: 100% !important; height: auto !important; max-width: 300px; }
#viewer3d { width: 100%; height: 320px; border-radius: var(--r); overflow: hidden; touch-action: none; }
#viewer3d canvas { display: block; width: 100%; height: 100%; }
.viewer-msg { padding: 2rem 1rem; text-align: center; color: var(--ink-faint); font-size: .9rem; }

.emoji-grid { display: flex; flex-wrap: wrap; gap: .3rem; }
.emoji-grid button {
  width: 38px; height: 38px; font-size: 19px; line-height: 1; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm);
}
.emoji-grid button:hover { border-color: var(--accent); }
.emoji-grid button[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent); }

.marco-opciones { margin-top: .6rem; }

.center-state { display: flex; align-items: center; gap: .6rem; font-size: .85rem; color: var(--ink-soft); margin-top: .5rem; }
.center-state img { width: 30px; height: 30px; object-fit: contain; border-radius: 4px; background: var(--surface-2); }

/* Subir logo. El objetivo de clic es el icono, no una fila con el control de
   archivo del sistema: ése lo pinta cada navegador a su manera y en español
   dice cosas distintas («Seleccionar archivo», «Elegir archivos», «Examinar»),
   así que no se puede escribir una etiqueta corta que cuadre en todos. El
   input va en .sr-only —sigue siendo enfocable con el teclado, por eso el
   foco se dibuja sobre el recuadro con :focus-within. */
.logo-up { display: flex; align-items: center; gap: .55rem; flex-wrap: wrap; margin-top: .7rem; }
.logo-up__tile {
  position: relative; flex: none; width: 38px; height: 38px; cursor: pointer;
  display: grid; place-items: center; color: var(--ink-faint);
  background: var(--surface); border: 1px dashed var(--line); border-radius: var(--r-sm);
}
.logo-up__tile svg { width: 19px; height: 19px; }
.logo-up__tile:hover { border-color: var(--accent); color: var(--accent); }
.logo-up__plus {
  position: absolute; top: -5px; right: -5px; width: 15px; height: 15px;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--accent); color: #fff;
  font-size: 11px; font-weight: 700; line-height: 1;
}
.logo-up__txt { font-size: .85rem; color: var(--ink-soft); cursor: pointer; }
.logo-up:focus-within .logo-up__tile { outline: 2px solid var(--accent); outline-offset: 2px; }
.logo-up-hint { flex-basis: 100%; font-size: .78rem; color: var(--ink-faint); }

.notices { display: grid; gap: .45rem; margin-top: .9rem; }
/* Bloque normal con el icono colgado a la izquierda, no flex: con flex cada
   <b> y cada trozo de texto entre ellos se volvía una columna aparte, y un
   aviso con tres negritas se leía en cinco columnas estrechas (avisos
   legales de firmar-pdf e hipoteca MX, encontrado el 2026-09-23). */
.notice {
  display: block; position: relative;
  font-size: .84rem; line-height: 1.45;
  padding: .6rem .75rem .6rem 2.15rem; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: var(--surface-2); color: var(--ink-soft);
}
/* Pase de diseño 2026-09-13: el aviso sólo se distinguía por el tinte de
   fondo/borde (verde/ámbar/rojo) — invisible para quien no distingue esos
   tonos, o si el aviso se lee sobre un fondo sin color (impreso en blanco y
   negro, un lector que fuerza alto contraste). Un icono de estado, en
   máscara CSS —no un <svg> en el HTML— para que las tres variantes compartan
   una sola regla y los avisos que escribe JS directamente en el DOM
   (#meta-error, #firma-error, #sn-aviso-tope...) lo hereden gratis por su
   clase, sin tocar ese código. El color lo pone `background-color` con el
   mismo token que ya usa el texto en negrita de cada variante; la forma la
   pone `mask-image`, así que sigue el tema claro/oscuro solo. */
.notice::before {
  content: ""; position: absolute; left: .75rem; top: calc(.6rem + .2em); width: 1.05em; height: 1.05em;
  background-color: var(--ink-faint);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='9'/><line x1='12' y1='11' x2='12' y2='16'/><circle cx='12' cy='7.7' r='.6' fill='%23000' stroke='none'/></svg>") center / contain no-repeat;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='9'/><line x1='12' y1='11' x2='12' y2='16'/><circle cx='12' cy='7.7' r='.6' fill='%23000' stroke='none'/></svg>") center / contain no-repeat;
}
.notice b { font-weight: 700; }
.notice--ok     { border-color: color-mix(in srgb, var(--ok) 35%, var(--line)); }
.notice--ok  b  { color: var(--ok); }
.notice--ok::before {
  background-color: var(--ok);
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='9'/><path d='m8.5 12.5 2.5 2.5 5-5'/></svg>");
          mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='9'/><path d='m8.5 12.5 2.5 2.5 5-5'/></svg>");
}
.notice--warn   { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 40%, var(--line)); }
.notice--warn b { color: var(--warn); }
.notice--warn::before {
  background-color: var(--warn);
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M12 3.5 21.5 20h-19z'/><line x1='12' y1='9.5' x2='12' y2='14'/><circle cx='12' cy='17' r='.6' fill='%23000' stroke='none'/></svg>");
          mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M12 3.5 21.5 20h-19z'/><line x1='12' y1='9.5' x2='12' y2='14'/><circle cx='12' cy='17' r='.6' fill='%23000' stroke='none'/></svg>");
}
.notice--bad    { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 40%, var(--line)); }
.notice--bad  b { color: var(--danger); }
.notice--bad::before {
  background-color: var(--danger);
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='9'/><path d='m9 9 6 6m0-6-6 6'/></svg>");
          mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='9'/><path d='m9 9 6 6m0-6-6 6'/></svg>");
}

.privacy-badge {
  display: flex; align-items: center; gap: .5rem; justify-content: center;
  margin: 1.1rem auto 0; max-width: 62ch; text-align: center;
  font-size: .88rem; color: var(--ink-soft);
}
.privacy-badge svg { width: 16px; height: 16px; flex: none; color: var(--ok); }

.downloads { display: grid; gap: .6rem; margin-top: 1.2rem; }
.downloads .pair { display: flex; flex-wrap: wrap; gap: .6rem; }
.downloads .pair > * { flex: 1 1 160px; }
.dl-note { font-size: .8rem; color: var(--ink-faint); margin: 0; }

.busy { font-size: .85rem; color: var(--ink-soft); display: none; align-items: center; gap: .5rem; }
.busy[data-on="1"] { display: flex; }
.spinner {
  width: 14px; height: 14px; border-radius: 50%; flex: none;
  border: 2px solid var(--line); border-top-color: var(--accent);
  animation: spin .8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2.4s; } }

/* ── 7. Secciones de contenido ─────────────────────────────────────────── */
.section { padding: 2.6rem 0; }
.section--tint { background: var(--surface-2); border-block: 1px solid var(--line-soft); }
.section > .wrap > h2 { text-align: center; }
.section .sub { text-align: center; color: var(--ink-soft); max-width: 60ch; margin: 0 auto 2rem; }

/* Rediseñada 2026-09-08: tres cajas idénticas de icono+título+texto son el
   patrón menos específico que existe (el propio auditor de impeccable lo
   nombra como "same-size icon+heading+text cards" — el contenedor perezoso
   por defecto). Aquí no hay tres tarjetas: hay UNA secuencia, con una línea
   que conecta los iconos y ningún borde/fondo propio por paso. El orden lo
   sigue dando el <ol>; el icono sigue sustituyendo al número (ver más abajo),
   ahora reforzado por la línea que sí dice "esto va en fila". */
.steps {
  display: grid; gap: 0 1.6rem; list-style: none; padding: 0; margin: 0;
  counter-reset: step; position: relative;
}
.steps li { counter-increment: step; min-width: 0; padding-bottom: 1.5rem; }
.steps li:last-child { padding-bottom: 0; }
.step-icon {
  display: grid; place-items: center;
  width: 40px; height: 40px; margin-bottom: .8rem;
  border-radius: 50%; background: var(--accent); color: var(--accent-ink);
  position: relative; z-index: 1;
}
.step-icon svg { width: 20px; height: 20px; }
.steps h3 { margin-bottom: .3rem; }
.steps p { margin: 0; font-size: .92rem; color: var(--ink-soft); }

/* Escritorio: fila de tres con la línea pasando por el centro de los
   iconos. El círculo, opaco, la tapa a su paso — no hace falta ocultar
   el tramo que queda debajo de cada icono. */
@media (min-width: 621px) {
  .steps {
    grid-template-columns: repeat(3, 1fr); gap: 0 1.6rem;
    text-align: center;
  }
  .steps::before {
    content: ""; position: absolute; top: 20px;
    left: calc(100% / 6); right: calc(100% / 6);
    height: 2px; background: var(--line);
  }
  .steps li { padding-bottom: 0; }
  .step-icon { margin-inline: auto; }
}

/* Móvil: la misma línea, vertical, a la izquierda de los iconos. */
@media (max-width: 620px) {
  .steps { grid-template-columns: 1fr; }
  .steps li { display: grid; grid-template-columns: 40px 1fr; column-gap: 1rem; position: relative; }
  .steps li::before {
    content: ""; position: absolute; left: 19px; top: 40px; bottom: 0;
    width: 2px; background: var(--line);
  }
  .steps li:last-child::before { display: none; }
  .step-icon { margin-bottom: 0; }
  .steps h3, .steps p { grid-column: 2; }
}

/* ── 6b. Lista de reglas (spec sheet) ──────────────────────────────────── */
/* Alternativa a .cards para contenido que NO es una lista de enlaces
   equivalentes (eso sí es un grid legítimo, ver el directorio de la
   portada): tres principios en paralelo no son tres tarjetas iguales, son
   una lista con jerarquía tipográfica — mismo lenguaje que .detalle-row y
   .dg-fila, que ya usan las calculadoras. Nada de iconos en círculo ni cajas
   con sombra; el peso lo lleva el propio texto. */
.rule-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.rule-list li { padding: 1.5rem 0; border-bottom: 1px solid var(--line); }
.rule-list h3 { margin: 0 0 .35rem; }
.rule-list p { margin: 0; color: var(--ink-soft); font-size: .95rem; max-width: 60ch; }
@media (min-width: 720px) {
  .rule-list li { display: grid; grid-template-columns: minmax(0, 260px) minmax(0, 1fr); gap: 1.8rem; align-items: baseline; }
  .rule-list h3 { margin: 0; }
}

.cards { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 1.25rem; }
.card h3 { margin-bottom: .35rem; }
.card p { margin: 0; font-size: .92rem; color: var(--ink-soft); }
.card--link { text-decoration: none; color: inherit; display: block; transition: border-color .15s ease, box-shadow .15s ease; }
.card--link:hover { border-color: var(--accent); box-shadow: var(--shadow-sm); }
.card--soon { opacity: .72; border-style: dashed; }

.prose { max-width: 68ch; margin-inline: auto; }
.prose h2 { text-align: left; }
/* Cuando el <h2> no es el único titular del bloque sino uno de varios
   apartados numerados seguidos dentro del mismo texto corrido (política de
   privacidad: 8; aviso legal: 9) — se distingue porque tiene un <h1> como
   hermano anterior en el mismo .prose, cosa que no pasa en la página de
   herramienta (ahí el <h2> abre el bloque y no hay <h1> dentro de .prose).
   Sin esto, la regla `.section > .wrap > h2` (centrado, Bricolage, más
   específica por accidente: dos clases igual que aquí pero un elemento
   menos) le ganaba siempre a la de arriba —nunca llegó a aplicarse—, y ocho
   titulares de sección seguidos, centrados y a tamaño de titular, se leían
   como eslóganes sueltos en vez de como un documento. Aquí vuelve a ser
   Inter, a la izquierda y a un tamaño de apartado, con aire arriba para que
   cada uno se note como el principio de algo nuevo sin gritarlo. */
.wrap.prose h1 ~ h2 {
  text-align: left; font-family: var(--sans); font-weight: 700;
  letter-spacing: normal; font-size: 1.2rem; margin-top: 2.3rem;
}
.prose h3 { margin-top: 1.6rem; }
.prose ul { padding-left: 1.2rem; }
.prose li { margin-bottom: .4rem; }

details.faq { border-bottom: 1px solid var(--line); padding: .35rem 0; }
details.faq summary { cursor: pointer; font-weight: 650; padding: .8rem 0; list-style: none; display: flex; justify-content: space-between; gap: 1rem; align-items: center; }
details.faq summary::-webkit-details-marker { display: none; }
details.faq summary::after { content: "+"; font-size: 1.3rem; color: var(--accent-text); line-height: 1; flex: none; }
details.faq[open] summary::after { content: "\2013"; }
details.faq > div { padding-bottom: 1rem; color: var(--ink-soft); }
details.faq > div > p:last-child { margin-bottom: 0; }

/* Apertura/cierre animados de <details> (FAQ, avanzado, extra-3d): puro
   CSS, sin JS. Primer intento: animar max-height con JS midiendo
   scrollHeight — el propio detector de impeccable lo marca como
   "layout-transition" (max-height obliga a recalcular layout en cada
   fotograma) y sugiere exactamente esta alternativa: grid-template-rows.
   <details> pasa a ser una rejilla de dos filas —resumen, contenido— y sólo
   la segunda anima entre 0fr (cerrado) y 1fr (abierto); el navegador
   interpola la altura resultante él solo, sin medir nada a mano.
   El único hueco que hay que tapar: por defecto el navegador pone
   `display: none` al contenido de un <details> cerrado, y eso no se puede
   animar (desaparece de golpe antes de que la fila llegue a 0). Forzar
   `display: block` en el <div> —siempre, abierto o cerrado— pisa esa regla;
   `overflow: hidden` es lo que recorta lo que haya dentro mientras la fila
   crece o encoge, sin importar cuántos <p> o campos traiga, así que no hace
   falta envolver nada ni tocar el HTML de cada página.
   Sin este bloque (un navegador muy viejo que ignore grid-template-rows en
   transition), <details> se sigue abriendo y cerrando bien: sólo que sin
   animar, porque 0fr/1fr siguen siendo valores válidos aunque no se puedan
   interpolar. */
details.faq, details.extra-3d, details.avanzado {
  display: grid; grid-template-rows: auto 0fr;
  transition: grid-template-rows .3s cubic-bezier(.22,.61,.36,1);
}
details.faq[open], details.extra-3d[open], details.avanzado[open] { grid-template-rows: auto 1fr; }
details.faq > div, details.extra-3d > div, details.avanzado > div { display: block; overflow: hidden; min-height: 0; }
@media (prefers-reduced-motion: reduce) {
  details.faq, details.extra-3d, details.avanzado { transition: none; }
}

/* ── 8. Huecos de publicidad (se entregan VACÍOS) ──────────────────────── */
/* Ocultos hasta que main.js ponga html.con-anuncios (manifest: ads: true).
   Así no se publica ninguna caja vacía con la palabra «Anuncio». */
html:not(.con-anuncios) .ad-slot,
html:not(.con-anuncios) aside.ad-toast,
html:not(.con-anuncios) dialog.ad-dialog { display: none !important; }
.ad-slot {
  display: grid; place-items: center;
  border: 1px dashed var(--line); border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--ink-faint);
  font-size: .72rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  margin-inline: auto;
}
.ad-slot--leaderboard { width: min(100%, 728px); min-height: 90px; margin-block: 2rem; }
.ad-slot--incontent   { width: min(100%, 580px); min-height: 250px; margin-block: 2rem; }
.ad-slot--dialog      { width: 100%; min-height: 250px; margin-block: 1rem; }
.ad-slot--toast       { width: 100%; min-height: 100px; }

dialog.ad-dialog { border: 1px solid var(--line); border-radius: var(--r-lg); padding: 0; background: var(--surface); color: var(--ink); box-shadow: var(--shadow); max-width: min(92vw, 400px); }
dialog.ad-dialog::backdrop { background: rgba(10, 9, 14, .55); }
dialog.ad-dialog .dlg-body { padding: 1.2rem 1.3rem 1.4rem; }
dialog.ad-dialog .dlg-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; }
dialog.ad-dialog h2 { font-size: 1.05rem; margin: 0 0 .2rem; }
dialog.ad-dialog p { font-size: .88rem; color: var(--ink-soft); margin: 0; }
.icon-btn {
  border: 0; background: none; cursor: pointer; color: var(--ink-faint);
  font-size: 1.4rem; line-height: 1; border-radius: 4px;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px; margin: -.1rem -.3rem; /* objetivo táctil real sin agrandar la fila visualmente */
}
.icon-btn:hover { color: var(--ink); }

aside.ad-toast {
  position: fixed; right: 1rem; bottom: 1rem; z-index: 50; width: min(92vw, 320px);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  box-shadow: var(--shadow); padding: .8rem;
}
aside.ad-toast[hidden] { display: none !important; }
aside.ad-toast .toast-top { display: flex; justify-content: space-between; align-items: center; gap: .5rem; margin-bottom: .5rem; }
aside.ad-toast .toast-top span { font-size: .7rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-faint); }

/* ── 8b. Fotografía y narrativa ────────────────────────────────────────── */

figure.shot { margin: 0; }
figure.shot img {
  display: block; width: 100%; height: auto;
  border-radius: var(--r); border: 1px solid var(--line-soft);
  background: var(--surface-2);
}
figure.shot figcaption {
  margin-top: .6rem; font-size: .84rem; color: var(--ink-faint); text-align: center;
}

/* Foto suelta dentro de un texto: hereda el ancho de .prose (~68ch), que es
   donde el detalle fino de la imagen todavía no se lee como texto. */
.prose figure.shot { margin: 1.6rem 0; }

.shot-grid { display: grid; gap: 1.2rem; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.shot-grid figure.shot img { aspect-ratio: 4 / 3; object-fit: cover; }

/* Comparador deslizante antes/después */
.ba {
  position: relative; overflow: hidden; user-select: none;
  border-radius: var(--r); border: 1px solid var(--line-soft);
  aspect-ratio: 4 / 3; background: var(--surface-2);
}
.ba img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ba .ba-after { clip-path: inset(0 0 0 var(--split, 50%)); }
.ba .ba-tag {
  position: absolute; bottom: .7rem; z-index: 3;
  font-size: .72rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  background: rgba(12,11,16,.72); color: #fff; padding: .3rem .6rem; border-radius: 999px;
  backdrop-filter: blur(4px); pointer-events: none;
}
.ba .ba-tag--l { left: .7rem; }
.ba .ba-tag--r { right: .7rem; }
.ba .ba-line {
  position: absolute; top: 0; bottom: 0; z-index: 2; width: 2px; margin-left: -1px;
  left: var(--split, 50%); background: #fff; box-shadow: 0 0 0 1px rgba(0,0,0,.25);
  pointer-events: none;
}
.ba .ba-line::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border-radius: 50%; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,.3);
}
.ba input[type="range"] {
  position: absolute; inset: 0; z-index: 4; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: ew-resize;
}
.ba input[type="range"]:focus-visible { opacity: .001; outline: 2px solid var(--accent); outline-offset: -2px; }

/* Grano sutil sobre las bandas de color: quita el plano de "plantilla" */
.has-grain { position: relative; }
.has-grain::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
  pointer-events: none; opacity: .14; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  background-size: 220px 220px;
}
.has-grain > * { position: relative; z-index: 1; }

/* Cabecera: transparente arriba, sólida al bajar */
.site-header { transition: box-shadow .25s ease, background-color .25s ease; }
.site-header.is-stuck { box-shadow: 0 1px 0 var(--line), 0 6px 24px rgba(15,14,20,.06); }

/* Botón principal con elevación al pasar por encima */
.btn--primary {
  box-shadow: 0 4px 14px rgba(0,0,0,.08), 0 1px 3px rgba(0,0,0,.06);
  transition: transform .35s cubic-bezier(.22,.61,.36,1), box-shadow .35s cubic-bezier(.22,.61,.36,1), filter .15s ease;
}
/* Sólo con puntero fino: en táctil, :hover se queda "pegado" tras el toque
   hasta el siguiente tap en otro sitio — un botón que se queda levantado en
   el aire después de pulsarlo parece roto, no elegante. */
@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover {
    transform: translateY(-3px);
    box-shadow: 0 18px 40px rgba(0,0,0,.16), 0 8px 18px color-mix(in srgb, var(--accent) 22%, transparent);
  }
}
.btn--primary:active { transform: translateY(-1px); transition-duration: .1s; }
@media (prefers-reduced-motion: reduce) {
  .btn--primary { transition: none; }
  .btn--primary:hover { transform: none; }
}

/* Bloque plegable genérico: nació sólo para el 3D de la página 2D del QR;
   desde 2026-09-08 también pliega "En el centro"/"Marco" en las dos páginas
   de QR — la personalización no tiene que competir por atención con lo que
   hace falta desde el primer segundo (contenido, estilo, descarga). Mismas
   reglas, sin duplicar CSS: el nombre de la clase se queda, el uso ya no es
   sólo 3D. */
details.extra-3d {
  margin-top: 1.4rem; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface-2); overflow: hidden;
}
details.extra-3d > summary {
  cursor: pointer; list-style: none; padding: .95rem 1.1rem;
  font-weight: 650; display: flex; align-items: center; gap: .6rem;
}
details.extra-3d > summary::-webkit-details-marker { display: none; }
details.extra-3d > summary::after {
  content: "+"; margin-left: auto; font-size: 1.35rem; line-height: 1; color: var(--accent-text);
}
details.extra-3d[open] > summary::after { content: "\2013"; }
details.extra-3d > summary .badge {
  font-size: .72rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  background: var(--accent); color: var(--accent-ink); padding: .2rem .45rem; border-radius: 4px;
}
details.extra-3d .extra-body { padding: 0 1.1rem 1.2rem; }
details.extra-3d .extra-note { font-size: .85rem; color: var(--ink-soft); margin: 0 0 1rem; }

/* Iconos en línea: tamaño explícito en TODOS los contextos (si no, revientan) */
.brand svg, .privacy-badge svg, .site-footer svg,
.steps h3 svg, .card h3 svg, .notice svg, details.extra-3d summary svg {
  width: 1.15em; height: 1.15em; flex: none;
}
.brand svg { width: 26px; height: 26px; }
.privacy-badge svg { width: 16px; height: 16px; }

/* ── 9. Reveals (sólo bajo el pliegue) ─────────────────────────────────── */
.reveal { opacity: 0; transform: translateY(14px); transition: opacity .5s ease, transform .5s ease; }
.reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; transition: none; } }
html:not(.is-ready) .reveal { opacity: 1; transform: none; }

/* Las listas de elementos repetidos (tarjetas, pasos, reglas) no entran en
   bloque: cada elemento se anima por separado con un pequeño retraso. Es el
   mismo disparador (.is-in, puesto por el único IntersectionObserver de
   main.js) — aquí sólo cambia DÓNDE vive la animación: en los hijos, no en
   el contenedor. El resto de .reveal (foto suelta, comparador) sigue
   entrando en bloque arriba: ahí no hay una lista que escalonar, y hacerlo
   en todas partes por igual sería el mismo problema que esto arregla. */
.cards.reveal, .steps.reveal, .rule-list.reveal, .type-grid.reveal, .tool-index.reveal { opacity: 1; transform: none; transition: none; }
.cards.reveal > *, .steps.reveal > li, .rule-list.reveal > li, .type-grid.reveal > *, .tool-index.reveal > li {
  opacity: 0; transform: translateY(14px);
  transition: opacity .5s cubic-bezier(.22,.61,.36,1), transform .5s cubic-bezier(.22,.61,.36,1);
}
.cards.reveal.is-in > *, .steps.reveal.is-in > li, .rule-list.reveal.is-in > li, .type-grid.reveal.is-in > *, .tool-index.reveal.is-in > li { opacity: 1; transform: none; }
.cards.reveal > *:nth-child(2), .steps.reveal > li:nth-child(2), .rule-list.reveal > li:nth-child(2), .type-grid.reveal > *:nth-child(2), .tool-index.reveal > li:nth-child(2) { transition-delay: .07s; }
.cards.reveal > *:nth-child(3), .steps.reveal > li:nth-child(3), .rule-list.reveal > li:nth-child(3), .type-grid.reveal > *:nth-child(3), .tool-index.reveal > li:nth-child(3) { transition-delay: .14s; }
.cards.reveal > *:nth-child(4), .type-grid.reveal > *:nth-child(4), .tool-index.reveal > li:nth-child(4) { transition-delay: .21s; }
.cards.reveal > *:nth-child(5), .type-grid.reveal > *:nth-child(5), .tool-index.reveal > li:nth-child(5) { transition-delay: .27s; }
.cards.reveal > *:nth-child(n+6), .type-grid.reveal > *:nth-child(n+6), .tool-index.reveal > li:nth-child(n+6) { transition-delay: .32s; }
@media (prefers-reduced-motion: reduce) {
  .cards.reveal > *, .steps.reveal > li, .rule-list.reveal > li, .type-grid.reveal > *, .tool-index.reveal > li { opacity: 1; transform: none; transition: none; }
}
html:not(.is-ready) .cards.reveal > *,
html:not(.is-ready) .steps.reveal > li,
html:not(.is-ready) .rule-list.reveal > li,
html:not(.is-ready) .type-grid.reveal > *,
html:not(.is-ready) .tool-index.reveal > li { opacity: 1; transform: none; }

/* ── 10. Sin JavaScript ────────────────────────────────────────────────── */
html.no-js .needs-js { display: none; }
.nojs-note { display: none; }
html.no-js .nojs-note { display: block; padding: 1.6rem; text-align: center; color: var(--ink-soft); }

/* ── 11. Impresión ─────────────────────────────────────────────────────
   Ctrl+P en la herramienta imprime SÓLO el código, grande y centrado.
   Sin cabecera, sin controles, sin huecos de anuncio y sin pie. */
@media print {
  .site-header, .site-footer, .section, .ad-slot, aside.ad-toast,
  dialog.ad-dialog, .privacy-badge, .downloads, #qr-lote, #notices,
  details.extra-3d, .tool-head p.lede, .tool-head p.dl-note,
  #qr-tool .tool-grid > .panel:first-child, .preview-box .cap, #viewer3d { display: none !important; }

  html, body { background: #fff !important; color: #000 !important; }
  .tool-card { border: 0 !important; box-shadow: none !important; padding: 0 !important; }
  .tool-grid { display: block !important; }
  .preview-box { border: 0 !important; background: none !important; padding: 0 !important; min-height: 0 !important; }
  .tool-head h1 { font-size: 14pt; text-align: center; margin-bottom: 8mm; }
  #qr-preview canvas, #qr-preview svg { width: 90mm !important; max-width: 90mm !important; margin-inline: auto; }
  @page { margin: 15mm; }
}

/* ── 12. Calculadora de hipoteca ───────────────────────────────────────── */

.panel-sep { border-top: 1px dashed var(--line); margin: 1.6rem 0 1.2rem; }
.panel-star { color: var(--accent-text) !important; }

/* En el móvil se intercala: primero la cifra que la persona vino a buscar,
   luego los campos para cambiarla, y el detalle y la gráfica después. Así se
   ve un resultado real Y se puede escribir sin hacer scroll.
   El panel de resultado se disuelve con display:contents para poder ordenar
   sus piezas por separado; no tiene nada enfocable, así que el orden de
   tabulación sigue siendo entrada → salida. */
@media (max-width: 899px) {
  #hipoteca-tool .tool-grid { display: flex; flex-direction: column; }
  #hipoteca-tool .tool-grid > .panel:nth-child(2) { display: contents; }
  #hipoteca-tool .tool-grid > .panel:nth-child(2) > h2 { display: none; }
  #hipoteca-tool .hero-ahorro { order: -3; margin-bottom: 1rem; }
  #hipoteca-tool .cifras { order: -2; margin-bottom: 1.4rem; }
  #hipoteca-tool .tool-grid > .panel:first-child { order: -1; }
  #hipoteca-tool .detalle-extra { order: 1; }
  #hipoteca-tool .gr-box { order: 2; }
}

/* Cada píxel de altura que ahorramos aquí acerca el primer campo al pliegue. */
@media (max-width: 620px) {
  .hero-ahorro { padding: .9rem 1rem; }
  .hero-note { font-size: .78rem; margin-top: .45rem !important; }
  /* Tres columnas fijas no caben: con importes de siete cifras la tercera
     caja se derramaba fuera de la tarjeta y quedaba cortada. Que se parta. */
  #hipoteca-tool .cifras { grid-template-columns: repeat(auto-fit, minmax(min(100%, 104px), 1fr)); gap: .45rem; }
  #hipoteca-tool .cifra { padding: .55rem .5rem; }
  #hipoteca-tool .cifra-k { font-size: .7rem; letter-spacing: .02em; }
  #hipoteca-tool .cifra-v { font-size: .95rem; }
}

/* Con tres herramientas el menú ya no cabe en una línea de móvil. */
@media (max-width: 620px) {
  .site-header .wrap { gap: .7rem; }
  .brand { padding-block: .35rem; }
  .site-footer .foot-links a, .site-footer .foot-tools a { padding-block: .45rem; }
  .btn--link { padding-block: .6rem; }
  .dl-note a, .tool-head a { display: inline-block; padding-block: .5rem; }
  .brand { flex: none; }
  .site-header nav {
    gap: .85rem; font-size: .84rem;
    flex: 1 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none;
    /* El degradado del borde derecho es la señal de que quedan enlaces. */
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
            mask-image: linear-gradient(to right, #000 calc(100% - 26px), transparent);
  }
  .site-header nav::-webkit-scrollbar { display: none; }
  .site-header nav a { white-space: nowrap; }
}

details.avanzado { margin: 0 0 1rem; }
details.avanzado > summary {
  cursor: pointer; list-style: none; font-size: .82rem; font-weight: 600;
  color: var(--ink-soft); padding: .5rem 0; display: flex; align-items: center; gap: .4rem;
}
details.avanzado > summary::-webkit-details-marker { display: none; }
details.avanzado > summary::before { content: "+"; color: var(--accent-text); font-size: 1.1rem; line-height: 1; }
details.avanzado[open] > summary::before { content: "\2013"; }

/* El titular: lo que la página existe para responder. */
.hero-ahorro {
  background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  border-radius: var(--r); padding: 1.2rem 1.3rem; margin-bottom: 1.2rem; text-align: center;
}
.hero-ahorro[data-on="0"] { display: none; }
.hero-ahorro p { margin: 0; }
/* Sin VERSALES: es una frase entera ("Si abonas a capital, te ahorras"), no
   una etiqueta de dos palabras — mayúsculas sostenidas la vuelven más lenta
   de leer justo encima del número que la persona vino a buscar. */
.hero-lead { font-size: .84rem; font-weight: 650; letter-spacing: .005em; color: var(--ink-soft); }
.hero-num {
  font-family: var(--display);
  font-size: clamp(2rem, 1.2rem + 3.4vw, 3rem); font-weight: 800; line-height: 1.05;
  letter-spacing: -.03em; color: var(--accent-text); margin: .15rem 0 .25rem !important;
  font-variant-numeric: tabular-nums;
}
.hero-sub { font-size: 1rem; color: var(--ink); }
.hero-sub b { color: var(--accent-text); }
.hero-note { font-size: .84rem; color: var(--ink-soft); margin-top: .6rem !important; line-height: 1.45; }

.cifras { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: .7rem; }
.cifra {
  background: var(--surface-2); border: 1px solid var(--line-soft);
  border-radius: var(--r-sm); padding: .75rem .85rem;
}
.cifra-k { display: block; font-size: .74rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-faint); }
.cifra-v { display: block; font-size: 1.15rem; font-weight: 750; letter-spacing: -.02em; margin-top: .2rem; font-variant-numeric: tabular-nums; }

.detalle-extra { margin-top: 1rem; }
.detalle-row {
  display: flex; justify-content: space-between; gap: 1rem; align-items: baseline;
  font-size: .88rem; color: var(--ink-soft); padding: .42rem 0; border-bottom: 1px solid var(--line-soft);
}
.detalle-row:last-child { border-bottom: 0; }
.detalle-row b { color: var(--ink); font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; }

.gr-box { margin: 1.4rem 0 0; }
.gr-box canvas { display: block; width: 100%; height: 240px; }
.gr-leyenda { display: flex; flex-wrap: wrap; gap: .5rem 1rem; align-items: center; font-size: .78rem; color: var(--ink-faint); margin-top: .5rem; }
.lg { display: inline-flex; align-items: center; gap: .4rem; }
.lg::before { content: ""; width: 16px; height: 0; border-top-width: 2px; border-top-style: solid; }
.lg--base::before { border-top-color: var(--ink-faint); border-top-style: dashed; }
.lg--extra::before { border-top-color: var(--accent); border-top-width: 3px; }
.gr-nota { flex: 1 1 100%; }

.aviso-legal-calc { max-width: 68ch; margin: 1rem auto 0; }

/* La tabla puede ser ancha en el móvil: se desliza sola, no rompe la página. */
.tabla-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
.tabla-vacia { margin: 0; padding: 2.2rem 1.2rem; text-align: center; color: var(--ink-faint); font-size: .9rem; }
.tabla-amort { width: 100%; border-collapse: collapse; font-size: .87rem; font-variant-numeric: tabular-nums; }
.tabla-amort th, .tabla-amort td { padding: .55rem .8rem; text-align: right; white-space: nowrap; }
.tabla-amort th:first-child, .tabla-amort td:first-child { text-align: left; }
.tabla-amort thead th {
  position: sticky; top: 0; background: var(--surface-2); color: var(--ink-soft);
  font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  border-bottom: 1px solid var(--line);
}
.tabla-amort tbody tr:nth-child(even) { background: var(--surface-2); }
.tabla-amort tbody td { border-bottom: 1px solid var(--line-soft); }
.tabla-amort tbody tr:last-child td { border-bottom: 0; }

.comparador { display: grid; gap: 1.2rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.cmp-col { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 1.2rem; }
.cmp-col h3 { font-size: .8rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-faint); margin-bottom: .9rem; }
.cmp-veredicto {
  text-align: center; margin: 1.4rem auto 0; max-width: 46ch;
  font-size: 1rem; color: var(--ink-soft);
}
.cmp-veredicto b { color: var(--accent-text); }

/* ── 12b. Calculadora de sueldo neto ───────────────────────────────────── */

.check { display: block; margin-bottom: 1rem; font-size: .9rem; padding-block: .35rem; }
.check input { width: 18px; height: 18px; margin-right: .5rem; vertical-align: -3px; accent-color: var(--accent); }
.check > span:not(.hint) { font-weight: 600; }
.check .hint { display: block; margin-top: .3rem; margin-left: 1.75rem; font-size: .78rem; color: var(--ink-faint); }

/* Barra apilada: sin librería de gráficas, sólo flex. */
.barra-box { margin: 1.4rem 0 0; }
.barra { display: flex; height: 40px; border-radius: var(--r-sm); overflow: hidden; background: var(--surface-2); }
/* flex-basis:0 es obligatorio: con «auto» los tramos que llevan texto dentro
   se quedan anchura de más y la barra deja de ser proporcional. */
.barra .tramo { display: grid; place-items: center; min-width: 0; flex-basis: 0; transition: flex-grow .25s ease; }
@media (prefers-reduced-motion: reduce) { .barra .tramo { transition: none; } }
.barra .tramo-pct { font-size: .74rem; font-weight: 700; letter-spacing: .01em; }
.tramo--neto { background: var(--accent); }
.tramo--neto .tramo-pct { color: var(--accent-ink); }
/* 58 %, no 55: con la repaleta 2026-09-06 (--ink/--surface-2 más fríos) el
   55 % daba 4,44:1 para el texto blanco encima, por debajo de AA. 58 % da
   4,88:1. Encontrado por el propio audit-diseno.js, no a ojo. */
.tramo--irpf { background: color-mix(in srgb, var(--ink) 58%, var(--surface-2)); }
.tramo--irpf .tramo-pct { color: var(--surface); }
.tramo--ss { background: color-mix(in srgb, var(--ink) 25%, var(--surface-2)); }
.tramo--ss .tramo-pct { color: var(--ink); }

.barra-leyenda { display: flex; flex-wrap: wrap; gap: .4rem 1.1rem; margin-top: .6rem; font-size: .78rem; color: var(--ink-soft); }
.barra-leyenda .lg::before { content: ""; width: 10px; height: 10px; border-radius: 3px; border: 0; }
.barra-leyenda .lg--neto::before { background: var(--accent); }
.barra-leyenda .lg--irpf::before { background: color-mix(in srgb, var(--ink) 58%, var(--surface-2)); }
.barra-leyenda .lg--ss::before   { background: color-mix(in srgb, var(--ink) 25%, var(--surface-2)); }
.barra-leyenda b { color: var(--ink); }

/* El desglose paso a paso */
.desglose-wrap { border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); overflow: hidden; }
.desglose { margin: 0; }
.dg-fila {
  display: flex; justify-content: space-between; align-items: baseline; gap: 1.2rem;
  padding: .75rem 1.1rem; border-bottom: 1px solid var(--line-soft);
}
.dg-fila:last-child { border-bottom: 0; }
.dg-fila dt { font-size: .92rem; color: var(--ink-soft); margin: 0; }
.dg-fila dd { margin: 0; font-variant-numeric: tabular-nums; font-weight: 650; white-space: nowrap; }
.dg-fila.is-fuerte { background: var(--surface-2); }
.dg-fila.is-fuerte dt { color: var(--ink); font-weight: 700; }
.dg-fila.is-fuerte dd { font-size: 1.05rem; font-weight: 800; }
.dg-nota { display: block; font-size: .76rem; color: var(--ink-faint); margin-top: .15rem; font-weight: 400; }

@media (max-width: 620px) {
  .dg-fila { padding: .65rem .8rem; gap: .8rem; }
  .dg-fila dt { font-size: .85rem; }
}

/* Mismo intercalado que la hipoteca: primero la cifra, luego los campos. */
@media (max-width: 899px) {
  #sueldo-tool .tool-grid { display: flex; flex-direction: column; }
  #sueldo-tool .tool-grid > .panel:nth-child(2) { display: contents; }
  #sueldo-tool .tool-grid > .panel:nth-child(2) > h2 { display: none; }
  #sueldo-tool .hero-ahorro { order: -3; margin-bottom: 1rem; }
  #sueldo-tool .cifras { order: -2; margin-bottom: 1.4rem; }
  #sueldo-tool .tool-grid > .panel:first-child { order: -1; }
  #sueldo-tool .barra-box { order: 1; }
  #sueldo-tool .notice { order: 2; }
  #sueldo-tool .detalle-row { order: 3; }
}

@media (max-width: 620px) {
  /* Tres columnas fijas no caben: con importes de siete cifras la tercera
     caja se derramaba fuera de la tarjeta y quedaba cortada. Que se parta. */
  #sueldo-tool .cifras { grid-template-columns: repeat(auto-fit, minmax(min(100%, 104px), 1fr)); gap: .45rem; }
  #sueldo-tool .cifra { padding: .55rem .5rem; }
  #sueldo-tool .cifra-k { font-size: .7rem; letter-spacing: .02em; }
  #sueldo-tool .cifra-v { font-size: .95rem; }
}

/* ── 12c. Limpiador de metadatos ───────────────────────────────────────── */

/* Los pasos se conmutan con data-state en la tarjeta. */
.paso { display: none; }
#meta-tool[data-state="vacio"]      .paso--vacio,
#meta-tool[data-state="trabajando"] .paso--trabajando,
#meta-tool[data-state="error"]      .paso--error,
#meta-tool[data-state="listo"]      .paso--listo { display: block; }

.zona {
  display: grid; place-items: center; gap: .35rem; text-align: center;
  padding: 2.8rem 1.5rem; cursor: pointer;
  border: 2px dashed var(--line); border-radius: var(--r);
  background: var(--surface-2); transition: border-color .15s ease, background .15s ease;
}
.zona.is-sobre { border-color: var(--accent); background: var(--accent-soft); }
@media (hover: hover) and (pointer: fine) {
  .zona:hover { border-color: var(--accent); background: var(--accent-soft); }
}
.zona svg { width: 42px; height: 42px; color: var(--accent); margin-bottom: .3rem; }
.zona-titulo { font-size: 1.1rem; font-weight: 700; }
.zona-sub { font-size: .92rem; color: var(--ink-soft); }
.zona-nota { font-size: .78rem; color: var(--ink-faint); margin-top: .3rem; }
@media (max-width: 620px) { .zona { padding: 2rem 1rem; } }

.meta-cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.6rem; }
@media (min-width: 900px) { .meta-cols { grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 2rem; } }

/* Lista de archivos cuando se sueltan varios */
.lista-img { display: grid; gap: .35rem; margin-bottom: 1.1rem; max-height: 190px; overflow-y: auto; }
.fila-img {
  display: flex; align-items: center; justify-content: space-between; gap: .8rem;
  width: 100%; text-align: left; cursor: pointer; font: inherit;
  padding: .5rem .7rem; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface); color: var(--ink);
}
.fila-img:hover { border-color: var(--ink-faint); }
.fila-img.is-activa { border-color: var(--accent); background: var(--accent-soft); }
.fila-nombre { font-size: .85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fila-tags { display: flex; gap: .35rem; flex: none; }
.tag-riesgo {
  font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: .15rem .4rem; border-radius: 4px; background: var(--surface-2); color: var(--ink-faint);
}
.tag-riesgo--alto { background: var(--danger-soft); color: var(--danger); }

/* Cada cosa que la foto revela */
.hallazgo {
  display: flex; gap: .8rem; align-items: flex-start;
  padding: .8rem .9rem; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: var(--surface); margin-bottom: .55rem;
}
.hallazgo--alto  { border-color: color-mix(in srgb, var(--danger) 40%, var(--line)); background: var(--danger-soft); }
.hallazgo--medio { border-color: color-mix(in srgb, var(--warn) 35%, var(--line)); }
.hallazgo-ico { flex: none; display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; background: var(--surface-2); color: var(--ink-soft); }
.hallazgo--alto  .hallazgo-ico { background: color-mix(in srgb, var(--danger) 18%, var(--surface)); color: var(--danger); }
.hallazgo--medio .hallazgo-ico { color: var(--warn); }
.hallazgo-ico svg { width: 17px; height: 17px; }
.hallazgo-cuerpo { min-width: 0; }
.hallazgo-clave { display: block; font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-faint); }
.hallazgo-valor { display: block; font-weight: 650; word-break: break-word; margin-top: .1rem; }
.hallazgo-valor a { font-weight: 600; font-size: .88rem; white-space: nowrap; }
.hallazgo-nota { display: block; font-size: .8rem; color: var(--ink-soft); margin-top: .25rem; line-height: 1.4; }

.limpia-ya {
  display: flex; align-items: center; gap: .6rem;
  padding: 1.1rem; border-radius: var(--r-sm);
  border: 1px solid color-mix(in srgb, var(--ok) 35%, var(--line));
  color: var(--ink-soft); font-size: .92rem; margin: 0;
}
.limpia-ya svg { width: 20px; height: 20px; flex: none; color: var(--ok); }
.limpia-ya--ojo { border-color: color-mix(in srgb, var(--warn) 40%, var(--line)); background: var(--warn-soft); }
.limpia-ya--ojo svg { color: var(--warn); }

.resumen { margin-bottom: 1rem; }
.res-fila {
  display: flex; justify-content: space-between; gap: 1rem; align-items: baseline;
  font-size: .88rem; color: var(--ink-soft);
  padding: .45rem 0; border-bottom: 1px solid var(--line-soft);
}
.res-fila:last-child { border-bottom: 0; }
.res-fila b { color: var(--ink); font-variant-numeric: tabular-nums; }

#meta-tool .downloads .btn { width: 100%; }

/* ── 12d. Firmar PDF ───────────────────────────────────────────────────── */

#firma-tool[data-state="vacio"]    .paso--vacio,
#firma-tool[data-state="cargando"] .paso--cargando,
#firma-tool[data-state="error"]    .paso--error,
#firma-tool[data-state="firmando"] .paso--firmando { display: block; }

.firma-cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.6rem; }
@media (min-width: 940px) { .firma-cols { grid-template-columns: minmax(0, 330px) minmax(0, 1fr); gap: 2rem; } }

.firma-pista { font-size: .86rem; color: var(--ink-soft); margin-bottom: .7rem; }

/* El recuadro de firma, con su renglón */
.pad-caja { position: relative; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); overflow: hidden; }
#firma-pad { display: block; width: 100%; height: 150px; touch-action: none; cursor: crosshair; }
.pad-linea { position: absolute; left: 8%; right: 8%; bottom: 34px; border-bottom: 1px dashed var(--line); pointer-events: none; }
#firma-pad[data-vacio="0"] ~ .pad-linea { opacity: 0; }

.pad-controles { display: grid; grid-template-columns: 1fr 1fr; gap: .9rem; margin-top: .9rem; align-items: end; }
.pad-controles fieldset { margin: 0; }
.pad-controles .field { margin-bottom: 0; }
@media (max-width: 480px) { .pad-controles { grid-template-columns: 1fr; } }

.firma-preview {
  display: block; max-width: 100%; max-height: 70px; margin-top: 1rem;
  padding: .5rem .7rem; border: 1px dashed var(--line); border-radius: var(--r-sm);
  background: var(--surface-2);
}

/* El visor del documento */
.visor { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r); padding: .9rem; overflow: auto; }
.visor-caja { position: relative; margin-inline: auto; width: max-content; max-width: 100%; }
#firma-pagina { display: block; box-shadow: var(--shadow-sm); background: #fff; }
.capa-firma { position: absolute; inset: 0; pointer-events: none; }

.sello {
  position: absolute; pointer-events: auto; cursor: grab;
  border: 1px dashed color-mix(in srgb, var(--accent) 70%, transparent);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  touch-action: none;
}
.sello:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.sello.is-moviendo { cursor: grabbing; border-style: solid; }
.sello img { display: block; width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
.sello-tirador {
  position: absolute; right: -7px; bottom: -7px; width: 15px; height: 15px;
  border-radius: 50%; background: var(--accent); border: 2px solid var(--surface);
  cursor: nwse-resize; touch-action: none;
}

.paginacion { display: flex; align-items: center; justify-content: center; gap: 1rem; margin-top: 1rem; font-size: .88rem; color: var(--ink-soft); }
.paginacion .btn { padding: .45rem .8rem; font-size: .85rem; }

/* ── 13. Impresión de la calculadora ───────────────────────────────────
   Ctrl+P en la hipoteca imprime la tabla de amortización con las cifras
   de cabecera y el aviso legal. Nada del formulario ni de la gráfica. */
@media print {
  #hipoteca-tool .tool-grid > .panel:first-child,
  #hipoteca-tool .gr-box,
  #hipoteca-tool .hero-note,
  #hp-imprimir { display: none !important; }

  #hipoteca-tool .tool-grid { display: block !important; }
  .hero-ahorro { background: none !important; border: 1px solid #999 !important; }
  .hero-num { color: #000 !important; }
  .hero-sub b { color: #000 !important; }
  .cifra { background: none !important; border: 1px solid #ccc !important; }

  #hp-tabla-sec { display: block !important; padding: 6mm 0 0 !important; }
  #hp-tabla-sec .sub, #hp-tabla-sec .dl-note { display: none !important; }
  #hp-tabla-sec h2 { font-size: 12pt; text-align: left; }
  .tabla-wrap { overflow: visible !important; border: 0 !important; }
  .tabla-amort { font-size: 8.5pt; }
  .tabla-amort thead { display: table-header-group; }   /* cabecera en cada hoja */
  .tabla-amort thead th { position: static !important; background: none !important; border-bottom: 1px solid #000 !important; }
  .tabla-amort tbody tr { break-inside: avoid; }
  .tabla-amort tbody tr:nth-child(even) { background: #f2f2f2 !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  .aviso-legal-calc {
    display: block !important; background: none !important;
    border: 0 !important; border-top: 1px solid #999 !important;
    font-size: 8pt !important; margin-top: 6mm !important; padding: 2mm 0 0 !important;
  }
  .aviso-legal-calc::before { display: none !important; }
}

/* ── 14. Impresión del sueldo neto ─────────────────────────────────────
   Imprime el neto y el desglose paso a paso: eso es lo que se lleva uno
   a la reunión o se guarda junto a la nómina. Nada del formulario. */
@media print {
  #sueldo-tool .tool-grid > .panel:first-child,
  #sueldo-tool .barra-box,
  #sn-imprimir { display: none !important; }

  #sueldo-tool .tool-grid { display: block !important; }

  #sn-desglose-sec { display: block !important; padding: 6mm 0 0 !important; }
  #sn-desglose-sec .sub, #sn-desglose-sec .dl-note { display: none !important; }
  #sn-desglose-sec h2 { font-size: 12pt; text-align: left; }
  .desglose-wrap { border: 0 !important; }
  .dg-fila { padding: 1.5mm 0 !important; border-bottom: 1px solid #ddd !important; break-inside: avoid; }
  .dg-fila.is-fuerte { background: none !important; border-bottom: 1px solid #000 !important; }
  .dg-nota { color: #555 !important; }
}

/* ── 15. Reflujo móvil: resultado primero ──────────────────────────────
   Encontrado por la crítica de impeccable (2026-09-08): el mismo reordenado
   que ya tenían hipoteca y sueldo neto —la respuesta aparece antes que los
   campos, en vez de obligar a bajar por todo el panel de entrada para
   verla— faltaba en el QR y en metadatos. En los dos el resultado se deriva
   en vivo de lo que hay en el panel de entrada, igual que en las
   calculadoras, así que el mismo patrón vale.

   Firmar PDF se queda FUERA a propósito: ahí no hay un resultado que se
   pueda adelantar. Dibujar la firma (panel 1) tiene que pasar antes de
   colocarla y descargar (panel 2) — no son campo+respuesta, son paso 1 y
   paso 2, y poner la descarga primero mostraría un botón que no puede
   funcionar todavía. */
@media (max-width: 899px) {
  #qr-tool .tool-grid { display: flex; flex-direction: column; }
  #qr-tool .tool-grid > .panel:nth-child(2) { display: contents; }
  #qr-tool .preview-box--qr { order: -4; margin-bottom: .8rem; }
  #qr-tool #notices { order: -3; }
  #qr-tool .downloads { order: -2; margin-bottom: 1rem; }
  #qr-tool .tool-grid > .panel:first-child { order: -1; }
  #qr-tool #qr-lote { order: 1; }
  #qr-tool #qr-reset-note { order: 2; }
}

@media (max-width: 899px) {
  #meta-tool .meta-cols { display: flex; flex-direction: column; }
  #meta-tool .meta-cols > .panel:nth-child(2) { display: contents; }
  #meta-tool .meta-cols > .panel:nth-child(2) > h2 { order: -4; }
  #meta-tool .resumen { order: -3; }
  #meta-tool #meta-metodo { order: -2; margin-bottom: 1rem; }
  #meta-tool .downloads { order: -1; margin-bottom: 1.4rem; }
  #meta-tool .meta-cols > .panel:first-child { order: 1; }
}

/* Enlaces de correo en las páginas legales: objetivo táctil cómodo en móvil. */
@media (max-width: 620px) {
  main a[href^="mailto:"] { display: inline-block; padding-block: .45rem; }
}
