/* ════════════════════════════════════════════════════════════════════════════
   Design Match — Fundación de la app instalada
   ────────────────────────────────────────────────────────────────────────────
   Todo lo de aquí vive bajo `html.dm-app-mode`. La web no lo ve. Eso es lo que
   permite rehacer la app siguiendo las guías de interfaz de Apple sin tocar la
   identidad visual del sitio: los mismos colores de marca, las mismas dos
   tipografías, otra retícula tipográfica y otro comportamiento.

   Qué corrige, medido en el navegador y no leído del CSS:

     · 19 tamaños de letra distintos en cinco pantallas (9, 10, 11, 12, 13, 15,
       16, 17, 19, 20, 21, 22, 23, 24, 25, 31.2, 34.32, 35.1, 38 px) y 10 pesos.
       Aquí hay once estilos con nombre y cinco pesos.
     · Todo en píxeles, así que nada respondía al tamaño de letra del sistema.
       Ahora la escala va en `rem` sobre una raíz que sigue a Dynamic Type.
     · 62 usos de peso 300. «In general, avoid light font weights… avoid
       Ultralight, Thin, and Light» (Typography > Ensuring legibility).
     · Etiquetas de la barra de pestañas a 9 px, cuando el mínimo del sistema
       para móvil son 11 pt (Typography, tabla de tamaños).
     · Once textos por debajo de 4.5:1, el peor a 2.3:1.
     · Diez controles por debajo de 44×44 pt.
     · Ni una línea de `prefers-color-scheme`. Una app instalada que sigue
       blanca a medianoche no parece del sistema.

   ── Por qué `rem` es seguro aquí ────────────────────────────────────────────
   No hay una sola unidad `rem` en todo el frontend —ni en el CSS, ni en los
   `<style>` de las páginas, ni en JS—. Comprobado con
   `grep -oE '[0-9]+(\.[0-9]+)?rem[;, )}]'` sobre todo `frontend/`: cero
   coincidencias. Así que mover el tamaño de la raíz no mueve nada más que lo
   que se declare en este archivo. Sin eso, Dynamic Type habría exigido tocar
   once hojas de estilo.

   ── Lo que NO se toca ───────────────────────────────────────────────────────
   La paleta: tinta #2B2B42, hueso #FFFEFA, oro #D8CC61, olivo #838B13, vino
   #4C072D. Las tipografías: Petrona para lo editorial, Inter para la interfaz.
   Adoptamos la ESCALA y el COMPORTAMIENTO del sistema, no su tipografía: poner
   San Francisco aquí haría que la app se pareciese a cualquier otra app de
   iPhone, que es justo lo contrario de lo que pide una marca.
   ════════════════════════════════════════════════════════════════════════════ */

/* ─────────────────────────────────────────────────────────────────────────────
   1 · Dynamic Type
   ─────────────────────────────────────────────────────────────────────────────
   106.25 % = 17/16. Con el ajuste de fábrica del navegador da los 17 px que el
   sistema pide por defecto en móvil, y si alguien ha subido el tamaño de letra
   base de su navegador, sube con él. En Safari, `app-mode.js` mide
   `font: -apple-system-body` y sobrescribe esta línea con el tamaño real de
   Dynamic Type, acotado entre 14 y 28 px (de xSmall a AX1). */
html.dm-app-mode {
  font-size: 106.25%;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

html.dm-app-mode {
  /* ── Escala tipográfica ───────────────────────────────────────────────────
     Los once estilos del sistema, con su interlineado. La relación entre
     ellos es lo que hace legible una jerarquía; los números sueltos no. */
  --t-large-title: 2rem;          --t-large-title-lh: 2.412rem;   /* 34/41 */
  --t-title-1: 1.647rem;          --t-title-1-lh: 2rem;           /* 28/34 */
  --t-title-2: 1.294rem;          --t-title-2-lh: 1.647rem;       /* 22/28 */
  --t-title-3: 1.176rem;          --t-title-3-lh: 1.471rem;       /* 20/25 */
  --t-headline: 1rem;             --t-headline-lh: 1.294rem;      /* 17/22 */
  --t-body: 1rem;                 --t-body-lh: 1.294rem;          /* 17/22 */
  --t-callout: .941rem;           --t-callout-lh: 1.235rem;       /* 16/21 */
  --t-subhead: .882rem;           --t-subhead-lh: 1.176rem;       /* 15/20 */
  --t-footnote: .765rem;          --t-footnote-lh: 1.059rem;      /* 13/18 */
  --t-caption-1: .706rem;         --t-caption-1-lh: .941rem;      /* 12/16 */
  /* El suelo va DENTRO del token, no en una regla aparte.
     Había una regla de suelo —`font-size: max(var(--t-caption-2), 11px)`—
     pero escrita con `:where()`, que vale cero de especificidad, así que
     cualquier regla con una clase la ganaba; y sólo tocaba `font-size`, que
     la forma corta `font:` pisa entera. Entre las dos cosas, las cifras del
     inicio pintaban «30 días» y «Total» a 10.999 px, por debajo del mínimo
     del sistema.
     Puesto aquí no hay especificidad que valga: el valor no puede ser menor.
     Y `max()` sólo pone piso, no techo, así que el texto sigue creciendo con
     el tamaño que cada quien tenga configurado. */
  --t-caption-2: max(.647rem, 11px);  --t-caption-2-lh: .765rem;  /* 11/13 */

  /* Cinco pesos. Ninguno por debajo de 400: los pesos finos son el motivo por
     el que un texto pequeño deja de leerse antes de tiempo. */
  --w-regular: 400;
  --w-medium: 500;
  --w-semibold: 600;
  --w-bold: 700;
  --w-heavy: 800;

  --f-ui: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --f-display: Petrona, "New York", ui-serif, Georgia, serif;

  /* ── Retícula de 4 pt, en `rem` ───────────────────────────────────────────
     El espaciado escala con el texto. Estaba en píxeles, y por eso subir el
     tamaño de letra apretaba las cajas hasta romperlas — que es la razón por
     la que Dynamic Type estaba topado tan abajo. Con el aire creciendo a la
     vez que la letra, el tope puede subir hasta el 200 % que pide la norma.

     Los valores son los mismos a tamaño normal: 4, 8, 12, 16, 20, 24, 32 y
     44 px sobre una raíz de 17. */
  --s-1: .235rem; --s-2: .471rem; --s-3: .706rem; --s-4: .941rem;
  --s-5: 1.176rem; --s-6: 1.412rem; --s-7: 1.882rem; --s-8: 2.588rem;

  /* Margen lateral del contenido. 16 pt es el margen estándar de una lista en
     móvil, y es lo que hace que todo caiga sobre la misma vertical. */
  --margen: .941rem;

  /* ── Radios ───────────────────────────────────────────────────────────────
     Medidos en la app de hoy: 12, 13, 14, 16, 18, 19, 20, 22, 25, 50 % y
     100 px conviviendo. Seis escalones bastan, y el más grande de una tarjeta
     no debería competir con el de la ventana. */
  --r-1: 8px;    /* etiquetas, campos pequeños */
  --r-2: 12px;   /* botones, celdas */
  --r-3: 16px;   /* tarjetas */
  --r-4: 22px;   /* contenedores grandes, barra de pestañas */
  --r-5: 28px;   /* hojas */
  --r-max: 999px;

  /* ── Blanco de toque ──────────────────────────────────────────────────────
     44×44 pt es el tamaño de control por defecto en móvil; 28×28 el mínimo
     absoluto (Accessibility > Mobility). Cuando el elemento tiene que verse
     más pequeño, se agranda el área con un pseudoelemento, no el dibujo. */
  --toque: 44px;   /* en px a propósito: es la medida de un dedo, no del texto */
}

/* ─────────────────────────────────────────────────────────────────────────────
   2 · Color semántico
   ─────────────────────────────────────────────────────────────────────────────
   Un color con nombre de función, no de tono. `--c-label-2` sabe cambiar entre
   claro y oscuro; `#555555` no. Los contrastes están medidos sobre el fondo
   que les toca, y van anotados para que nadie tenga que volver a calcularlos.
   ───────────────────────────────────────────────────────────────────────────── */
html.dm-app-mode {
  /* Superficies. `bg` es el lienzo; `bg-2` agrupa dentro de él; `bg-3` agrupa
     dentro de `bg-2` (Color > Platform considerations, jerarquía de fondos). */
  --c-bg: #FFFEFA;
  --c-bg-2: #F4F1E8;
  --c-bg-3: #EAE7DC;
  --c-bg-agrupado: #F2EFE6;
  --c-elevado: #FFFEFA;

  /* Texto. Los cuatro escalones del sistema, con su medida sobre --c-bg. */
  --c-label: #2B2B42;      /* 13.63:1 · cualquier tamaño */
  --c-label-2: #555555;    /*  7.39:1 · cualquier tamaño */
  /* 5.45:1 sobre el fondo de la página y 4.77:1 sobre `--c-bg-agrupado`.
     Las dos cifras hacen falta: estaba en #6F6F6F, «el más callado que aún
     cumple 4.5:1», pero ese 4.98:1 estaba medido contra la página. Sobre una
     baldosa —que es más oscura— el mismo gris da 4.37:1, y ahí es donde vive:
     «30 días», «Total» y el cero de las cifras del inicio, todos a 11 px.
     Un token validado contra un fondo y usado sobre otro no está validado. */
  --c-label-3: #696969;
  --c-label-4: #8A8A8A;    /*  3.43:1 · NUNCA para texto: iconos y adornos */
  --c-placeholder: #6F6F6F;

  /* Separadores y rellenos */
  --c-separador: rgba(43, 43, 66, .13);
  --c-separador-opaco: #E2DFD5;
  --c-relleno: rgba(43, 43, 66, .06);
  --c-relleno-2: rgba(43, 43, 66, .10);
  --c-relleno-3: rgba(43, 43, 66, .15);

  /* Acento. El olivo de marca da 3.67:1 sobre hueso: vale para un relleno
     grande, no para texto. `--c-tinte` es el mismo olivo bajado hasta 5.21:1,
     que es el que se puede leer; `--c-tinte-fondo` conserva el original para
     cuando hace de fondo y el texto va encima en hueso. */
  --c-tinte: #6B7110;          /* 5.21:1 sobre hueso */
  --c-tinte-fondo: #838B13;    /* fondo de marca; texto encima en --c-bg */
  --c-acento: #D8CC61;         /* oro: adorno y estado, nunca texto en claro */
  --c-acento-texto: #6B6115;   /* 6.22:1 — el oro cuando tiene que leerse */
  --c-primario: #4C072D;       /* vino: 15.13:1, la acción principal */

  /* Estados */
  --c-error: #C93A1E;          /* 5.07:1 (el #DD4426 de marca da 4.22:1) */
  --c-exito: #286447;          /* 6.93:1 */
  --c-aviso: #8A5A00;          /* 5.42:1 */

  /* ── Material ─────────────────────────────────────────────────────────────
     Aquí faltaba media referencia. La variante regular «blurs AND ADJUSTS THE
     LUMINOSITY of background content», y la lista de propiedades a replicar
     pide «adaptive tint: detect underlying content brightness, shift tint
     accordingly». Lo que había desenfocaba y saturaba, y después pintaba un
     velo DEL MISMO COLOR QUE LA PÁGINA. Sobre un fondo plano —y el de esta app
     lo es— desenfocar un color plano da ese mismo color: el dock desaparecía.

     Medido en píxeles sobre /vacantes, que no tiene nada debajo del dock:
     ΔL entre el dock y la página justo encima era 0.7 en claro y 7.7 en
     oscuro. O sea, ninguna. En /explorar, con una foto detrás, sí se leía —
     porque ahí el desenfoque tenía algo que hacer.

     Así que el material gana las dos piezas que le faltaban: un
     desplazamiento de luminancia (`--m-luz`) y la luz del canto superior
     (`--m-brillo`), que es lo que de verdad se lee como cristal. No vuelven
     los degradados radiales de antes: eso era brillo pintado, esto es el
     canto. */
  --m-veil: rgba(252, 251, 246, .72);
  /* Por debajo de 1 en claro: una superficie esmerilada sobre papel blanco
     recoge una sombra mínima, no más blanco. */
  --m-luz: .975;
  --m-brillo: rgba(255, 255, 255, .85);
  --m-brillo-ctrl: rgba(255, 255, 255, .30);
  --m-blur: 34px;
  /* El de los dos controles flotantes. 34 px es el desenfoque de una barra de
     ancho completo; en una pastilla de 50 px difumina tanto que el material
     deja de leerse como cristal y se ve como plástico opaco. */
  --m-blur-ctrl: 18px;
  --m-sat: 1.35;
  --m-borde: rgba(43, 43, 66, .09);
  --m-sombra: 0 8px 30px rgba(30, 31, 48, .10), 0 1px 2px rgba(30, 31, 48, .06);
  --m-separador: rgba(43, 43, 66, .12);

  color-scheme: light;
  accent-color: var(--c-tinte);
}

/* ── Oscuro ─────────────────────────────────────────────────────────────────
   No es el claro invertido: es el mismo papel de noche. Reportado como «muy
   desagradable a la vista», y medido en OKLCH la causa era de tono, no de
   contraste: el fondo salía del navy de marca (#14141C, tono 285, croma
   .016, que se ve violeta) y el acento era el oro de marca a croma plena
   (#D8CC61, tono 103, croma .129). Esos dos tonos están a 180° —son
   complementarios— y a esa saturación vibran uno contra otro: cada botón
   dorado sobre violeta parecía fosforito. Además había un tercer tono, el
   malva de `--c-primario`, en «Para ti».

   Ahora el fondo es un casi negro neutro con un pelo de calidez (tono 107,
   croma .004: el de la tinta del logo, 31·31·28) y las superficies siguen el
   tono del hueso del modo claro (92-95). El oro conserva su tono de marca
   (100) con un tercio menos de croma: se reconoce, pero no deslumbra. Y es
   el único acento: `--c-primario` también es oro en oscuro, así que cada
   acción principal se ve igual. El texto baja de #FFFEFA a #F1F0EC, 16.45:1
   en lugar de 18.15:1: el blanco puro sobre casi negro es lo que cansa. */
@media (prefers-color-scheme: dark) {
  html.dm-app-mode {
    --c-bg: #121210;
    --c-bg-2: #1D1C19;
    --c-bg-3: #272624;
    --c-bg-agrupado: #0C0B0A;
    --c-elevado: #1E1D1A;

    /* Medido contra las cinco superficies; la cifra es la peor, sobre bg-3. */
    --c-label: #F1F0EC;      /* 13.26:1 (16.45:1 sobre la página) */
    --c-label-2: #C1BFBA;    /*  8.23:1 */
    --c-label-3: #9D9B95;    /*  5.44:1 */
    --c-label-4: #6D6C67;    /*  2.87:1 · sólo adorno */
    --c-placeholder: #9D9B95;

    --c-separador: rgba(241, 240, 236, .13);
    --c-separador-opaco: #2B2A27;
    --c-relleno: rgba(241, 240, 236, .07);
    --c-relleno-2: rgba(241, 240, 236, .12);
    --c-relleno-3: rgba(241, 240, 236, .18);

    /* oklch(.854 .080 100). 9.76:1 sobre bg-3, y la tinta encima —que es
       `--c-bg`— 12.11:1. */
    --c-tinte: #DBD194;
    --c-tinte-fondo: #838B13;
    --c-acento: #DBD194;
    --c-acento-texto: #DBD194;
    --c-primario: #DBD194;

    --c-error: #F08A72;
    --c-exito: #7FC9A0;
    --c-aviso: #E5B65C;

    /* En oscuro el cristal es MÁS CLARO que lo que tiene detrás, no más
       oscuro: es una película que recoge luz. El velo de antes —`rgba(28,28,40,
       .88)` sobre una página `#14141C`— era casi el mismo color, y con eso el
       dock y los dos controles se leían como plástico apagado. El velo es del
       tono de la página: uno azulado sobre un fondo neutro volvería a teñir
       de violeta la barra. */
    --m-veil: rgba(78, 77, 73, .52);
    --m-luz: 1.5;
    --m-brillo: rgba(255, 255, 255, .16);
    --m-brillo-ctrl: rgba(255, 255, 255, .10);
    --m-borde: rgba(255, 254, 250, .10);
    --m-sombra: 0 8px 34px rgba(0, 0, 0, .40), 0 1px 2px rgba(0, 0, 0, .3);
    --m-separador: rgba(255, 254, 250, .12);

    color-scheme: dark;
  }

  html.dm-app-mode { background: var(--c-bg); }
  html.dm-app-mode body { background: var(--c-bg); color: var(--c-label); }
}

/* ── Contraste aumentado ────────────────────────────────────────────────────
   «If your app doesn't provide this minimum contrast by default, ensure it at
   least provides a higher contrast color scheme when the system setting
   Increase Contrast is turned on» (Accessibility > Vision). Aquí el mínimo ya
   se cumple por defecto, así que esto sube el escalón: los grises callados se
   acercan al texto principal, los separadores dejan de ser insinuaciones y el
   cristal pierde translucidez. */
@media (prefers-contrast: more) {
  html.dm-app-mode {
    --c-label-2: #3A3A47;
    --c-label-3: #4A4A55;
    --c-label-4: #5C5C66;
    --c-separador: rgba(43, 43, 66, .38);
    --c-separador-opaco: #B9B5A8;
    --c-relleno-2: rgba(43, 43, 66, .18);
    --c-tinte: #4E530C;
    --m-veil: rgba(255, 254, 250, .97);
    --m-luz: 1;
    --m-brillo: transparent;
    --m-brillo-ctrl: transparent;
    --m-borde: rgba(43, 43, 66, .3);
  }

  @media (prefers-color-scheme: dark) {
    html.dm-app-mode {
      --c-label-2: #EDEBE4;
      --c-label-3: #D5D3CB;
      --c-label-4: #B4B2AA;
      --c-separador: rgba(241, 240, 236, .38);
      --c-separador-opaco: #3E3D3A;
      --c-tinte: #EBE2A8;          /* 11.50:1 sobre bg-3 */
      --c-acento: #EBE2A8;
      --c-acento-texto: #EBE2A8;
      --c-primario: #EBE2A8;
      --m-veil: rgba(18, 18, 16, .97);
      --m-luz: 1;
      --m-brillo: transparent;
      --m-brillo-ctrl: transparent;
      --m-borde: rgba(255, 254, 250, .3);
    }
  }
}

/* ── Transparencia reducida ─────────────────────────────────────────────────
   El cristal es el material del sistema, pero es también lo primero que
   estorba a quien lo ha desactivado. Sin `backdrop-filter` la barra pasa a ser
   una superficie opaca con un separador: pierde la gracia, no la función. */
@media (prefers-reduced-transparency: reduce) {
  html.dm-app-mode {
    --m-veil: var(--c-bg);
    --m-blur: 0px;
    --m-blur-ctrl: 0px;
    --m-sat: 1;
    --m-luz: 1;
    --m-brillo: transparent;
    --m-brillo-ctrl: transparent;
  }
  /* La cabecera de marca queda fuera a propósito: ahí no hay superficie que
     volver opaca, y pintarle un fondo la convertiría otra vez en una barra.
     Lo que se vuelve opaco son los dos controles, que sí son superficies. */
  html.dm-app-mode .dm-liquid-glass,
  html.dm-app-mode #dm-app-header:not(.es-marca),
  html.dm-app-mode .dm-app-control,
  html.dm-app-mode #dm-app-bottom-nav {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    background: var(--c-bg) !important;
  }
}

/* ─────────────────────────────────────────────────────────────────────────────
   3 · Tipografía aplicada
   ─────────────────────────────────────────────────────────────────────────────
   El cuerpo de la app arranca en `body` (17 pt) y sube o baja desde ahí. Los
   selectores de más abajo recuperan lo que hoy se escapa: DM Sans, que se cuela
   desde `dashboard-memberships-v2.css` y convierte la app en tres tipografías
   a la vez —«Minimize the number of typefaces you use» (Typography > Conveying
   hierarchy)—, y los pesos por debajo de 400.
   ───────────────────────────────────────────────────────────────────────────── */
html.dm-app-mode body {
  font-family: var(--f-ui);
  font-size: var(--t-body);
  line-height: var(--t-body-lh);
  font-weight: var(--w-regular);
  color: var(--c-label);
  background: var(--c-bg);
  letter-spacing: -.01em;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

/* DM Sans no es una tipografía de la marca: entra de rebote con la hoja de
   membresías, que fija `body { font-family: "DM Sans" }`. En la app se anula. */
html.dm-app-mode body,
html.dm-app-mode .dash-body,
html.dm-app-mode .memb-shell,
html.dm-app-mode .memb-shell * { font-family: var(--f-ui); }

html.dm-app-mode h1, html.dm-app-mode h2, html.dm-app-mode h3,
html.dm-app-mode h4, html.dm-app-mode h5, html.dm-app-mode h6,
html.dm-app-mode .dm-app-titulo-grande { font-family: var(--f-display); }

html.dm-app-mode .dm-t-large-title { font: var(--w-bold) var(--t-large-title)/var(--t-large-title-lh) var(--f-display); letter-spacing: -.022em; }
html.dm-app-mode .dm-t-title-1     { font: var(--w-bold) var(--t-title-1)/var(--t-title-1-lh) var(--f-display); letter-spacing: -.02em; }
html.dm-app-mode .dm-t-title-2     { font: var(--w-semibold) var(--t-title-2)/var(--t-title-2-lh) var(--f-display); letter-spacing: -.016em; }
html.dm-app-mode .dm-t-title-3     { font: var(--w-semibold) var(--t-title-3)/var(--t-title-3-lh) var(--f-display); letter-spacing: -.012em; }
html.dm-app-mode .dm-t-headline    { font: var(--w-semibold) var(--t-headline)/var(--t-headline-lh) var(--f-ui); letter-spacing: -.011em; }
html.dm-app-mode .dm-t-body        { font: var(--w-regular) var(--t-body)/var(--t-body-lh) var(--f-ui); letter-spacing: -.011em; }
html.dm-app-mode .dm-t-callout     { font: var(--w-regular) var(--t-callout)/var(--t-callout-lh) var(--f-ui); letter-spacing: -.008em; }
html.dm-app-mode .dm-t-subhead     { font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui); letter-spacing: -.005em; }
html.dm-app-mode .dm-t-footnote    { font: var(--w-regular) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui); letter-spacing: 0; }
html.dm-app-mode .dm-t-caption-1   { font: var(--w-regular) var(--t-caption-1)/var(--t-caption-1-lh) var(--f-ui); letter-spacing: 0; }
html.dm-app-mode .dm-t-caption-2   { font: var(--w-medium) var(--t-caption-2)/var(--t-caption-2-lh) var(--f-ui); letter-spacing: .006em; }

html.dm-app-mode .dm-c-label   { color: var(--c-label); }
html.dm-app-mode .dm-c-label-2 { color: var(--c-label-2); }
html.dm-app-mode .dm-c-label-3 { color: var(--c-label-3); }
html.dm-app-mode .dm-c-tinte   { color: var(--c-tinte); }

/* Suelo de tamaño. Nada de la app puede bajar de 11 pt, que es el mínimo del
   sistema para móvil. Lo que hoy está a 9 y a 10 sube; lo que está por encima
   no se toca, porque `max()` se queda con el mayor. */
html.dm-app-mode :where(small, .dm-app-nav-item, .dm-app-nav-item span,
  .vtag, .vbadge, .stpill, .chip, .fchip, figcaption,
  .dm-gs-score span, .dm-gs-score small, .dm-gs-card-tags) {
  font-size: max(var(--t-caption-2), 11px);
}

/* Los pesos finos, al mínimo legible. */
html.dm-app-mode :where(h1, h2, h3, h4, h5, h6, p, span, a, li, td, th, strong,
  em, small, button, label, div)[style*="font-weight:3"],
html.dm-app-mode .hero-title,
html.dm-app-mode .listing-hero h1,
html.dm-app-mode .vtag { font-weight: max(400, var(--w-regular)); }

/* ─────────────────────────────────────────────────────────────────────────────
   4 · Material — Liquid Glass
   ─────────────────────────────────────────────────────────────────────────────
   Lo que había era brillo: dos degradados radiales, un destello diagonal y un
   `mix-blend-mode: screen` encima. Eso es cristal de 2013. El material del
   sistema «has no inherent color; it takes on colors from the content directly
   behind it» (Liquid Glass > Visual Properties): no aporta luces propias, se
   limita a desenfocar, saturar y dejar pasar.

   Los números salen de la referencia: desenfoque 20–40 px y opacidad 0.6–0.8
   para la variante regular, saturación 1.2–1.5.

   La regla que más importa aquí es dónde NO va: «Don't use Liquid Glass in the
   content layer». El cristal es de la cabecera, la barra de pestañas y las
   hojas. Las tarjetas van opacas.
   ───────────────────────────────────────────────────────────────────────────── */
html.dm-app-mode .dm-liquid-glass {
  position: relative;
  isolation: isolate;
  color: var(--c-label);
  background: var(--m-veil);
  border: 0;
  box-shadow:
    var(--m-sombra),
    inset 0 0 0 .5px var(--m-borde),
    inset 0 .5px 0 0 var(--m-brillo);
  -webkit-backdrop-filter: blur(var(--m-blur)) saturate(var(--m-sat)) brightness(var(--m-luz));
  backdrop-filter: blur(var(--m-blur)) saturate(var(--m-sat)) brightness(var(--m-luz));
}

/* Se retiran los tres brillos. Están escritos en `app-mode.css` con
   `::before` y `::after`; aquí se apagan en vez de borrarse allí para que el
   archivo antiguo siga siendo legible como lo que fue. */
html.dm-app-mode .dm-liquid-glass::before,
html.dm-app-mode .dm-liquid-glass::after,
html.dm-app-mode #dm-app-bottom-nav::before,
html.dm-app-mode #dm-app-bottom-nav::after { content: none; }

@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  html.dm-app-mode .dm-liquid-glass,
  html.dm-app-mode #dm-app-header:not(.es-marca),
  html.dm-app-mode .dm-app-control,
  html.dm-app-mode #dm-app-bottom-nav { background: var(--c-bg); }
}

/* ─────────────────────────────────────────────────────────────────────────────
   5 · Blanco de toque
   ─────────────────────────────────────────────────────────────────────────────
   Diez controles medidos por debajo de 44×44 pt: los chips de filtro a 36 px
   de alto, la paginación a 34×34, el desplegable de orden a 80×27, «Ver todos»
   a 56×12 y las flechas de pestañas a 30×48.

   Dos maneras de arreglarlo, y la elección importa: cuando el control puede
   crecer, crece. Cuando tiene que verse pequeño, se le pone un pseudoelemento
   que extiende el área sensible sin mover el dibujo. Agrandar el dibujo de un
   chip de filtro habría descolocado la fila entera.
   ───────────────────────────────────────────────────────────────────────────── */
html.dm-app-mode :where(button, a[href], [role="button"], select, summary,
  input[type="checkbox"], input[type="radio"]) {
  touch-action: manipulation;
}

html.dm-app-mode .fchip,
html.dm-app-mode .chip,
html.dm-app-mode .sort-sel,
html.dm-app-mode .search-btn,
html.dm-app-mode .empty-btn,
html.dm-app-mode .xcta-btn,
html.dm-app-mode .pg-btn,
/* «Ver perfil» de una tarjeta de Guardados: 124×30. La sección se medía con la
   cuenta de pruebas sin nada guardado, así que la tarjeta no existía y no
   fallaba nada; con dos perfiles dentro salen cuatro controles cortos. */
html.dm-app-mode .sp-ver {
  min-height: var(--toque);
}

html.dm-app-mode .pg-btn { min-width: var(--toque); }

/* Los que tienen que seguir pequeños: el área crece, el dibujo no. */
html.dm-app-mode :where(.nov-all, .tabs-scroll-arrow, .dm-app-header-btn,
  .dmt-saltar, .see-all, .link-mas) { position: relative; }

/* La ✕ de una tarjeta guardada mide 26×26 y tiene que seguir midiéndolo: es
   una marca en la esquina, no un botón. Ya va `position: absolute`, que por sí
   sola es caja de referencia para su propio pseudoelemento, así que no entra
   en el grupo de arriba. */
html.dm-app-mode :where(.nov-all, .tabs-scroll-arrow, .dmt-saltar,
  .see-all, .link-mas, .sp-unsave)::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, var(--toque));
  height: max(100%, var(--toque));
  transform: translate(-50%, -50%);
}

/* ─────────────────────────────────────────────────────────────────────────────
   5b · La campana y el centro de avisos
   ─────────────────────────────────────────────────────────────────────────────
   La campana reutiliza `.dm-app-bar-btn` y `.dm-app-control`: es el mismo
   control que el menú y el «+», con el mismo cristal y el mismo blanco de
   toque. Lo único suyo es el punto.

   El punto no lleva número. En un icono de 24 px un «12» se lee peor que una
   mancha, y una cifra obliga a decidir qué se cuenta —que es justo lo que
   estaba mal—. La cifra se dice dentro de la hoja, escrita.
   ───────────────────────────────────────────────────────────────────────── */
/* El grupo de la derecha. Sin él, `space-between` reparte tres hijos y la
   campana acaba en el centro, encima de la marca. */
html.dm-app-mode .dm-app-bar-dcha { display: flex; align-items: center; gap: 2px; }
html.dm-app-mode .dm-app-bar-campana { position: relative; }
html.dm-app-mode .dm-app-bar-punto {
  position: absolute; top: 9px; right: 9px;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--c-error, #C93A1E);
  /* Un aro del color de la barra: sobre el icono, un punto a hueso perdido se
     confunde con el trazo de la campana. */
  box-shadow: 0 0 0 2px var(--c-bg, #FFFEFA);
}
html.dm-app-mode .dm-app-bar-punto[hidden] { display: none; }

html.dm-app-mode .dm-app-sheet.es-avisos .dm-app-sheet-content { padding-top: var(--s-1, 4px); }
html.dm-app-mode .dm-avisos-vacio {
  margin: var(--s-6, 24px) 0;
  text-align: center;
  font: var(--w-regular, 400) var(--t-subhead, 15px)/1.4 var(--f-ui, Inter, sans-serif);
  color: var(--c-label-2);
}
/* Con la línea de debajo, el título es el titular del vacío. */
html.dm-app-mode .dm-avisos-vacio:has(+ .dm-avisos-vacio-sub) {
  margin-bottom: var(--s-1, 4px);
  font-weight: var(--w-semibold, 600);
  color: var(--c-label);
}
html.dm-app-mode .dm-avisos-vacio-sub {
  max-width: 30ch;
  margin: 0 auto var(--s-6, 24px);
  text-align: center;
  font: var(--w-regular, 400) var(--t-footnote, 13px)/1.45 var(--f-ui, Inter, sans-serif);
  color: var(--c-label-2);
  text-wrap: balance;
}
/* «No pudimos actualizarlas»: se enseña encima de la lista guardada. No es un
   error rojo —la lista sirve— sino una advertencia de que puede estar vieja. */
html.dm-app-mode .dm-avisos-viejo {
  margin: 0 0 var(--s-2, 8px);
  padding: var(--s-2, 8px) var(--s-3, 12px);
  border-radius: var(--r-2, 10px);
  background: var(--c-relleno-3, rgba(120, 120, 128, .12));
  font: var(--w-regular, 400) var(--t-footnote, 13px)/1.35 var(--f-ui, Inter, sans-serif);
  color: var(--c-label-2);
}
html.dm-app-mode .dm-avisos-todas {
  display: block; width: 100%; min-height: var(--toque, 44px);
  margin: 0 0 var(--s-2, 8px); padding: 0;
  border: 0; background: none; cursor: pointer;
  font: var(--w-medium, 500) var(--t-subhead, 15px)/1.2 var(--f-ui, Inter, sans-serif);
  color: var(--c-tinte);
  text-align: left;
}
html.dm-app-mode .dm-avisos-todas:disabled { opacity: .5; }

html.dm-app-mode .dm-avisos-lista { list-style: none; margin: 0; padding: 0; }
html.dm-app-mode .dm-avisos-item {
  display: flex; align-items: stretch; gap: var(--s-1, 4px);
  border-bottom: .5px solid var(--c-separador);
}
html.dm-app-mode .dm-avisos-item:last-child { border-bottom: 0; }
html.dm-app-mode .dm-avisos-fila {
  flex: 1; min-width: 0; display: flex; align-items: flex-start; gap: var(--s-3, 12px);
  min-height: var(--toque, 44px); padding: var(--s-3, 12px) 0;
  border: 0; background: none; text-align: left; text-decoration: none; cursor: pointer;
  color: var(--c-label);
}
/* El punto de «sin leer» ocupa sitio siempre, para que el texto de todas las
   filas empiece en la misma columna: si sólo lo tuvieran las nuevas, la lista
   se movería de lado al marcarlas. */
html.dm-app-mode .dm-avisos-punto {
  flex: none; width: 8px; height: 8px; margin-top: 6px; border-radius: 50%;
  background: transparent;
}
html.dm-app-mode .dm-avisos-item.es-nueva .dm-avisos-punto { background: var(--c-error, #C93A1E); }
html.dm-app-mode .dm-avisos-texto { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
html.dm-app-mode .dm-avisos-texto strong {
  font: var(--w-medium, 500) var(--t-subhead, 15px)/1.3 var(--f-ui, Inter, sans-serif);
  color: var(--c-label);
}
html.dm-app-mode .dm-avisos-item.es-nueva .dm-avisos-texto strong { font-weight: var(--w-semibold, 600); }
html.dm-app-mode .dm-avisos-texto span {
  font: var(--w-regular, 400) var(--t-footnote, 13px)/1.45 var(--f-ui, Inter, sans-serif);
  color: var(--c-label-2);
}
html.dm-app-mode .dm-avisos-texto small {
  font: var(--w-regular, 400) var(--t-caption-1, 12px)/1.2 var(--f-ui, Inter, sans-serif);
  color: var(--c-label-3);
}
html.dm-app-mode .dm-avisos-chevron { flex: none; align-self: center; color: var(--c-label-4); }
/* Los iconos de la app se dibujan a trazo, y el trazo lo pone quien los usa:
   `icon()` devuelve el `<svg>` pelado. Sin esto, el chevron sale como un
   triángulo negro relleno y la palomita como una mancha. */
html.dm-app-mode .dm-avisos-chevron svg,
html.dm-app-mode .dm-avisos-leida svg {
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
html.dm-app-mode .dm-avisos-chevron svg { width: 16px; height: 16px; }
html.dm-app-mode .dm-avisos-leida {
  flex: none; width: var(--toque, 44px); align-self: center;
  height: var(--toque, 44px); padding: 0;
  display: grid; place-items: center;
  border: 0; border-radius: var(--r-max, 999px); background: none;
  color: var(--c-label-3); cursor: pointer;
}
html.dm-app-mode .dm-avisos-leida svg { width: 18px; height: 18px; }
html.dm-app-mode .dm-avisos-leida:active { background: var(--c-relleno); }

/* ─────────────────────────────────────────────────────────────────────────────
   6 · Foco y movimiento
   ───────────────────────────────────────────────────────────────────────────── */
html.dm-app-mode :where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--c-tinte);
  outline-offset: 2px;
  border-radius: var(--r-1);
}

html.dm-app-mode ::selection { background: color-mix(in srgb, var(--c-acento) 45%, transparent); color: var(--c-label); }
html.dm-app-mode ::placeholder { color: var(--c-placeholder); opacity: 1; }

/* «Tracking animations directly with people's gestures», «Replacing
   transitions in x-, y-, and z-axes with fades» (Accessibility > Cognitive).
   Lo que ya había cortaba toda la animación; esto además evita el desenfoque,
   que la guía nombra explícitamente. */
/* ── Movimiento reducido ──────────────────────────────────────────────────
   Esto era `.01ms !important` sobre todo, que es el atajo de siempre y está
   mal: apaga también el feedback que ayuda a entender. Quien pide menos
   movimiento no está pidiendo una interfaz muda.

   «Reduced motion doesn't mean no feedback — it means a gentler,
   non-vestibular equivalent… Keep opacity/color changes that aid
   comprehension» (Apple Design > Reduced motion).

   Lo que se va: desplazamientos, escalados, rebotes y desenfoques — el
   movimiento que se siente en el oído interno. Lo que se queda: los fundidos
   cortos y los cambios de color, que son los que dicen que algo pasó. */
@media (prefers-reduced-motion: reduce) {
  html.dm-app-mode * ,
  html.dm-app-mode *::before,
  html.dm-app-mode *::after {
    /* Ni traslación, ni escala, ni rotación: sólo lo que se desvanece. */
    transition-property: opacity, color, background-color, border-color, box-shadow, fill, stroke !important;
    transition-duration: 140ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }

  /* La pulsación deja de encoger y pasa a responder con relleno: sigue
     habiendo respuesta al dedo, que es lo que pedía el principio de que la
     interfaz conteste en el `pointerdown` y no al soltar. */
  html.dm-app-mode .dm-app-pressable:active,
  html.dm-app-mode body :is(button, a[href], [role="button"], .cat-item, .fchip,
    .dm-gs-entry-cta, .dm-app-sheet-action):active {
    transform: none !important;
    background-color: var(--c-relleno-2);
  }

  /* La hoja aparece y desaparece con un fundido, no subiendo desde abajo. */
  html.dm-app-mode .dm-app-sheet { transform: none !important; }
  html.dm-app-mode .dm-app-sheet-layer { transition: opacity 160ms ease; }
  html.dm-app-mode .dm-app-sheet-layer:not(.is-open) { opacity: 0; }

  /* Y la cápsula de la barra de pestañas cambia de sitio sin recorrerlo. */
  html.dm-app-mode .dm-app-nav-indicator { transition: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   7 · Barra de navegación con título grande
   ───────────────────────────────────────────────────────────────────────────
   El patrón del sistema: el título de la pantalla empieza grande, dentro del
   contenido, y se recoge dentro de la barra cuando se hace scroll. No es un
   adorno — es lo que permite que la barra esté vacía y transparente mientras
   se lee, y sólo se materialice cuando hay algo debajo que tapar. Es la
   traducción literal del scroll edge effect: «Background content is blurred
   and reduced in opacity at scroll edges to enhance legibility»
   (Liquid Glass > Visual Properties).

   Antes: la cabecera llevaba siempre un título de 15 px centrado y el
   contenido arrancaba con un titular editorial distinto. Dos títulos para una
   pantalla.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode #dm-app-header {
  min-height: calc(50px + var(--dm-app-safe-top, 0px));
  padding: calc(3px + var(--dm-app-safe-top, 0px)) max(var(--margen), var(--dm-app-safe-right, 0px)) 3px max(var(--margen), var(--dm-app-safe-left, 0px));
  border-radius: 0;
  border-bottom: .5px solid transparent;
  background: transparent;
  /* Ver más abajo: la de marca no lleva ni ese borde transparente. */
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  transition:
    background-color var(--dm-motion-fast) ease,
    border-color var(--dm-motion-fast) ease,
    -webkit-backdrop-filter var(--dm-motion-fast) ease,
    backdrop-filter var(--dm-motion-fast) ease;
}

/* En reposo la barra no existe. En cuanto el contenido pasa por debajo, se
   condensa: cristal, hairline y el título pequeño.
   `:not(.es-marca)` porque la cabecera de las cinco pestañas no es una barra:
   son tres piezas sueltas y el scroll no puede juntarlas en una cápsula. Ahí
   el cristal vive en los dos controles y no se mueve. */
html.dm-app-mode #dm-app-header:not(.es-marca).is-condensada {
  background: var(--m-veil);
  border-bottom-color: var(--m-separador);
  -webkit-backdrop-filter: blur(var(--m-blur)) saturate(var(--m-sat)) brightness(var(--m-luz));
  backdrop-filter: blur(var(--m-blur)) saturate(var(--m-sat)) brightness(var(--m-luz));
}

html.dm-app-mode .dm-app-header-title {
  font: var(--w-semibold) var(--t-callout)/var(--t-callout-lh) var(--f-ui);
  letter-spacing: -.01em;
  color: var(--c-label);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--dm-motion-fast) ease, transform var(--dm-motion-fast) var(--dm-motion-curve);
}

/* El título pequeño no aparece al hacer scroll: aparece cuando el título
   grande ha dejado de verse. Son dos momentos distintos y confundirlos deja
   los dos títulos en pantalla a la vez. */
html.dm-app-mode #dm-app-header.is-titulada .dm-app-header-title {
  opacity: 1;
  transform: none;
}

/* El logo de Inicio no se recoge: es la marca, no un título de pantalla. */
html.dm-app-mode .dm-app-header-brand { height: 21px; opacity: 1; }

html.dm-app-mode .dm-app-header-btn {
  width: var(--toque);
  height: var(--toque);
  color: var(--c-tinte);
  border-radius: var(--r-max);
}
html.dm-app-mode .dm-app-header-btn svg { width: 22px; height: 22px; stroke-width: 1.8; }
html.dm-app-mode .dm-app-header-btn:active { background: var(--c-relleno); }

/* El título grande. Va en el contenido, no en la barra, y por eso puede ser
   de verdad grande sin robarle altura a la pantalla en cuanto se hace scroll. */
html.dm-app-mode .dm-app-titulo-grande {
  padding: var(--s-2) var(--margen) var(--s-3);
  margin: 0;
}
html.dm-app-mode .dm-app-titulo-grande h1 {
  margin: 0;
  font: var(--w-bold) var(--t-large-title)/var(--t-large-title-lh) var(--f-display);
  letter-spacing: -.022em;
  color: var(--c-label);
  text-wrap: balance;
}
html.dm-app-mode .dm-app-titulo-grande p {
  margin: var(--s-1) 0 0;
  max-width: 34ch;
  font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui);
  color: var(--c-label-2);
}

/* ═══════════════════════════════════════════════════════════════════════════
   8 · Barra de pestañas
   ───────────────────────────────────────────────────────────────────────────
   Se conserva la cápsula del elemento activo —una forma, no sólo un color:
   «Avoid relying solely on color to differentiate between objects» (Color >
   Inclusive color)— y además se tiñe el icono y la etiqueta, que es lo que la
   guía del material pide para un estado seleccionado: «Foreground color…
   Best for selected states (e.g., active tab bar item)».

   Las etiquetas suben de 9 px a 11, que es el mínimo del sistema.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode #dm-app-bottom-nav {
  /* ── El dock no puede ocupar la pantalla ────────────────────────────────
     La geometría del dock vive en `app-mode.css` —`position:fixed`, `bottom`,
     `height:68px`— y aquí se sobreescribía con `height:auto`. Una sola de las
     dos hojas aplicada da un bloque estático, oscuro, redondeado y del alto de
     sus cinco hijos: exactamente la pantalla rota que se reportó en iPhone.
     `position` y un tope de alto se repiten aquí a propósito, para que
     ninguna combinación de hojas pueda estirarlo. */
  /* La geometría entera, no sólo `position`: si esta hoja se aplicara sin la
     otra, un `fixed` sin referencias se clava donde le pille.

     PERO repetirla en crudo se llevó por delante las franjas seguras. Esta
     hoja carga después y gana por especificidad (`html.dm-app-mode #id` contra
     `#id`), así que estos tres valores planos anulaban los de `app-mode.css`,
     que sí las miran. Medido en el navegador con
     `--dm-app-safe-bottom: 34px` —el indicador de inicio de un iPhone con
     notch—: el dock se quedaba a 10 px del borde físico, o sea justo encima
     del indicador, en los cuatro modelos y también en apaisado, donde las
     franjas laterales son de 21 px y el dock se metía bajo el notch.

     Y el `var()` lleva respaldo —`, 0px`— porque el patrón «simple primero»
     protege de UNA sola cosa: que Safari no entienda `max()` y descarte la
     declaración AL LEERLA, con lo que sobrevive la de antes. No protege de la
     otra: si `--dm-app-safe-bottom` no existe, la declaración ya ganó la
     cascada y muere al CALCULARSE, y entonces la propiedad no vuelve a la
     anterior — se queda en `unset`, o sea `bottom: auto`. Medido sirviendo
     `app-mode.css` vacía (que es donde se declara la variable): el dock
     `fixed` sin `bottom` se queda en su posición estática, y en el panel eso
     fue `bottom: -3893.56px`, o sea 3.894 px POR DEBAJO de la pantalla.
     Irrecuperable, y sin un solo error en consola.

     Se repite el mismo par de declaraciones que usa `app-mode.css`: primero
     la simple y después la buena. Una declaración con `max()` y `calc()`
     dentro que Safari no entienda se tira ENTERA, y sin la simple delante un
     `bottom` tirado deja el dock `fixed` sin referencia — clavado donde le
     pille, que durante el arranque es debajo de la cabecera. Ésa es la barra
     enorme arriba de la pantalla que se reportó desde iPhone. */
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: 10px;
  left: max(12px, calc(var(--dm-app-safe-left, 0px) + 8px));
  right: max(12px, calc(var(--dm-app-safe-right, 0px) + 8px));
  bottom: max(10px, calc(var(--dm-app-safe-bottom, 0px) + 8px));
  height: auto;
  min-height: 56px;
  max-height: 96px;
  padding: var(--s-1);
  border-radius: var(--r-4);
  background: var(--m-veil);
  box-shadow: var(--m-sombra), inset 0 0 0 .5px var(--m-borde), inset 0 .5px 0 0 var(--m-brillo);
  -webkit-backdrop-filter: blur(var(--m-blur)) saturate(var(--m-sat)) brightness(var(--m-luz));
  backdrop-filter: blur(var(--m-blur)) saturate(var(--m-sat)) brightness(var(--m-luz));
}

html.dm-app-mode .dm-app-nav-indicator {
  top: var(--s-1);
  bottom: var(--s-1);
  left: var(--s-1);
  width: calc((100% - var(--s-2)) / var(--dm-app-nav-total, 5));
  border-radius: calc(var(--r-4) - var(--s-1));
  background: var(--c-relleno-2);
  border: 0;
  box-shadow: none;
}
html.dm-app-mode .dm-app-nav-indicator.is-moving { border-radius: calc(var(--r-4) - var(--s-1)); filter: none; }

html.dm-app-mode .dm-app-nav-item {
  min-height: var(--toque);
  gap: 2px;
  padding: var(--s-1) 2px;
  border-radius: calc(var(--r-4) - var(--s-1));
  color: var(--c-label-3);
  /* Con techo: al 200 % `--t-caption-2` son 22 px y «Vacantes» se partía en
     dos renglones dentro de una barra de alto fijo. El techo son 13 px porque
     es donde «Guardados» —la etiqueta más larga— todavía entra en un quinto
     de 390 px. Subirlo a 15 hacía la letra más grande y el texto más corto,
     que es justo lo contrario de lo que pide quien agranda la letra. */
  font: var(--w-medium) clamp(11px, var(--t-caption-2), 13px)/1.1 var(--f-ui);
  letter-spacing: 0;
}
html.dm-app-mode .dm-app-nav-item > span {
  /* El techo tiene que ir AQUÍ y no sólo en el `font` de la pestaña: la regla
     de tamaños mínimos de §5 apunta a `.dm-app-nav-item span` directamente, y
     una regla sobre el hijo gana siempre a lo que hereda del padre. */
  font-size: clamp(11px, var(--t-caption-2), 13px);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Cuando el dock se pone oscuro, sus rótulos también.
  
   `.dm-app-nav-item` fija aquí un escalón de la escala de la página, y esa
   escala no sabe de qué color es el cristal que tiene debajo. Sobre el velo
   claro funciona; en cuanto el dock cambia a `data-glass-context="dark"`
   —lo hace sobre contenido oscuro, y en el editor de perfil pasa siempre—
   quedaban los cuatro rótulos inactivos en gris oscuro sobre casi negro:
   1.63:1 medido, con 4.5 de mínimo.
  
   Quien sabe el color del fondo es el cristal, así que el rótulo lo hereda de
   él. `--glass-icon` ya vale `rgba(255,254,250,.72)` en ese contexto, que es
   justo para lo que existe. */
/* SÓLO el contexto «dark», y no también «photo» y «color».
  
   Los otros dos componen su velo con `--c-bg` —`color-mix(… var(--c-bg) 86%)`—,
   así que ese velo ya gira con la apariencia y la tinta semántica de la escala
   es la correcta. `--glass-icon`, en cambio, es un literal del `:root` con
   tinta oscura, y esos dos contextos no lo redefinen: aplicándoselo, en
   apariencia oscura quedaban los cuatro rótulos en tinta oscura sobre un velo
   oscuro. 1.19:1 medido — peor que el 1.63 del que se venía. */
html.dm-app-mode #dm-app-bottom-nav[data-glass-context="dark"] .dm-app-nav-item:not([aria-current="page"]) {
  color: var(--glass-icon);
}

html.dm-app-mode .dm-app-nav-item svg { width: 23px; height: 23px; stroke-width: 1.7; }
html.dm-app-mode .dm-app-nav-item[aria-current="page"] { color: var(--c-tinte); filter: none; }
html.dm-app-mode .dm-app-nav-item[aria-current="page"] svg { stroke-width: 2; }

/* ── «Voy» no es «ya estoy» ──────────────────────────────────────────────────
   Una pestaña que lleva a otro documento no puede marcarse al tocarla: la
   ruta aún no ha cambiado y puede no cambiar. Pero el dedo necesita saber que
   se le oyó, así que el toque deja esta marca —el color de la sección al 55 %,
   sin engordar el trazo— y la cápsula NO se mueve. Cuando el documento nuevo
   monta, la marca de verdad aparece allí.

   Sin animación a propósito: un latido aquí sería un cronómetro pidiendo
   paciencia, y lo que hay que arreglar es la espera, no decorarla. */
html.dm-app-mode .dm-app-nav-item[data-dm-en-camino]:not([aria-current="page"]) {
  color: color-mix(in oklab, var(--c-tinte) 55%, var(--c-label-2));
}

/* Cuando la cuenta no llega a las cinco pestañas, la cápsula tiene que medir
   lo que mida una pestaña de verdad. Sin esto se quedaba en un quinto fijo. */
html.dm-app-mode #dm-app-bottom-nav[data-tabs="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
html.dm-app-mode #dm-app-bottom-nav[data-tabs="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
html.dm-app-mode #dm-app-bottom-nav[data-tabs="5"] { grid-template-columns: repeat(5, minmax(0, 1fr)); }

/* ═══════════════════════════════════════════════════════════════════════════
   9 · Listas agrupadas
   ───────────────────────────────────────────────────────────────────────────
   La lista de categorías de Buscar ya era lo mejor de la app; le faltaba el
   acabado del sistema: fondo agrupado, tarjeta con esquinas, separadores
   metidos hasta donde empieza el texto —no de borde a borde, que es lo que
   hace que una lista parezca una tabla— y filas de 44 pt.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode .dm-lista {
  margin: 0 var(--margen) var(--s-6);
  padding: 0;
  list-style: none;
  border-radius: var(--r-3);
  background: var(--c-elevado);
  box-shadow: inset 0 0 0 .5px var(--c-separador);
  overflow: hidden;
}
html.dm-app-mode .dm-lista > * {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-height: var(--toque);
  padding: var(--s-3) var(--s-4);
  color: var(--c-label);
  text-decoration: none;
}
html.dm-app-mode .dm-lista > * + *::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--s-4);
  right: 0;
  height: .5px;
  background: var(--c-separador);
}
html.dm-app-mode .dm-lista > *:active { background: var(--c-relleno); }

html.dm-app-mode .dm-lista-titulo {
  margin: var(--s-6) var(--margen) var(--s-2);
  font: var(--w-semibold) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui);
  color: var(--c-label-2);
  text-transform: none;
  letter-spacing: 0;
}

/* ═══════════════════════════════════════════════════════════════════════════
   10 · Capa de contenido
   ───────────────────────────────────────────────────────────────────────────
   Opaca, siempre. «Don't use Liquid Glass in the content layer… Including it
   in the content layer creates unnecessary complexity and confusing visual
   hierarchy» (Liquid Glass > General Rules). Sombras suaves en vez de las
   sombras largas de la web: en una pantalla de teléfono una sombra de 44 px
   sólo ensucia.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body :is(.vcard, .pcard, .card, .icard, .dm-gs-card, .saved-card, .job-card) {
  border-radius: var(--r-3);
  background: var(--c-elevado);
  border: .5px solid var(--c-separador);
  box-shadow: none;
}

html.dm-app-mode body :is(.dm-app-editorial-feature) { border-radius: var(--r-3); box-shadow: none; }

/* Los chips: forma de píldora, altura de control, y el activo con relleno
   sólido en vez de un tono a medio camino. */
html.dm-app-mode body :is(.fchip, .chip, .vtag, .dm-gs-chip) {
  border-radius: var(--r-max);
  font: var(--w-medium) var(--t-subhead)/1.1 var(--f-ui);
  color: var(--c-label-2);
  border: .5px solid var(--c-separador);
  background: transparent;
}
html.dm-app-mode body :is(.fchip.on, .chip.on, .dm-gs-chip[aria-pressed="true"]) {
  background: var(--c-label);
  border-color: var(--c-label);
  color: var(--c-bg);
}

/* ═══════════════════════════════════════════════════════════════════════════
   11 · Entrada de datos
   ───────────────────────────────────────────────────────────────────────────
   16 px es el suelo real de un campo en iOS: por debajo, Safari hace zoom al
   enfocar y deja la pantalla descuadrada. No es una preferencia estética.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode :where(input, select, textarea) {
  font-size: max(var(--t-callout), 16px);
  font-family: var(--f-ui);
  color: var(--c-label);
}

html.dm-app-mode body :is(.search-input, input[type="search"], input[type="text"], input[type="email"]) {
  min-height: var(--toque);
}

/* El campo de búsqueda del sistema: píldora, relleno tenue, lupa dentro. */
html.dm-app-mode .dm-app-buscador {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin: 0 var(--margen) var(--s-4);
  padding: 0 var(--s-3);
  min-height: var(--toque);
  border-radius: var(--r-2);
  background: var(--c-relleno);
  color: var(--c-label-3);
}
html.dm-app-mode .dm-app-buscador svg { width: 18px; height: 18px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 2; }
html.dm-app-mode .dm-app-buscador input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  padding: 0;
  min-height: var(--toque);
  font: var(--w-regular) max(var(--t-callout), 16px)/1.2 var(--f-ui);
  color: var(--c-label);
}
html.dm-app-mode .dm-app-buscador input:focus { outline: none; }
html.dm-app-mode .dm-app-buscador:focus-within { box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-tinte) 35%, transparent); }

/* ═══════════════════════════════════════════════════════════════════════════
   12 · Hojas
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode .dm-app-sheet {
  border-radius: var(--r-5) var(--r-5) 0 0;
  background: var(--c-elevado);
  box-shadow: 0 -8px 40px rgba(0, 0, 0, .18);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
html.dm-app-mode .dm-app-sheet-backdrop { background: rgba(0, 0, 0, .32); }
html.dm-app-mode .dm-app-sheet-handle { background: var(--c-relleno-3); }
html.dm-app-mode .dm-app-sheet-title { font: var(--w-semibold) var(--t-title-2)/var(--t-title-2-lh) var(--f-display); color: var(--c-label); }
html.dm-app-mode .dm-app-sheet-copy { font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui); color: var(--c-label-2); }
html.dm-app-mode .dm-app-sheet-close { background: var(--c-relleno); color: var(--c-label-2); }
html.dm-app-mode .dm-app-sheet-action {
  min-height: var(--toque);
  border-radius: var(--r-2);
  background: var(--c-relleno);
  color: var(--c-label);
  font: var(--w-medium) var(--t-body)/var(--t-body-lh) var(--f-ui);
}
html.dm-app-mode .dm-app-sheet-action:active { background: var(--c-relleno-2); }
html.dm-app-mode .dm-app-sheet-action.is-danger { color: var(--c-error); }

/* ═══════════════════════════════════════════════════════════════════════════
   13 · Botones
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body :is(.dm-app-primary, .dm-app-secondary, .search-btn, .empty-btn, .xcta-btn) {
  min-height: 50px;
  border-radius: var(--r-2);
  font: var(--w-semibold) var(--t-headline)/1.2 var(--f-ui);
  letter-spacing: -.01em;
  text-transform: none;
}
/* La tinta es `--c-bg` y no hueso: en claro es lo mismo (#FFFEFA sobre vino),
   y en oscuro `--c-primario` es el oro, donde el hueso encima daría 1.3:1. */
html.dm-app-mode .dm-app-primary { background: var(--c-primario); color: var(--c-bg); box-shadow: none; }
html.dm-app-mode .dm-app-primary::after { content: none; }
html.dm-app-mode .dm-app-secondary { background: var(--c-relleno-2); color: var(--c-label); box-shadow: none; }
html.dm-app-mode .dm-app-auth-link { color: var(--c-tinte); font: var(--w-medium) var(--t-subhead)/1.2 var(--f-ui); }

/* ═══════════════════════════════════════════════════════════════════════════
   14 · Lo que sobra dentro de una app
   ───────────────────────────────────────────────────────────────────────────
   Nada de esto se borra del sitio: se apaga cuando la app está instalada.

   · Migas de pan. Son de una web con URLs; en una app la vuelta atrás es el
     gesto y el botón de la barra. Tener las dos cosas obliga a elegir.
   · El titular editorial y la cita de las pestañas herramienta. Ocupaban
     ~450 px antes del primer control de Vacantes, en una pantalla de 844.
     «Make essential information easy to find by giving it sufficient space»
     (Layout > Best practices).
   · La fila de accesos rápidos de Inicio, que repite cuatro de las cinco
     pestañas de la barra inferior, con los mismos iconos y los mismos nombres.
   · Las VERSALITAS. «Is sentence case used for UI text (not ALLCAPS…)».
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body :is(.dm-migas, .hero-eyebrow, .hero-quote, .dm-app-quick-actions) { display: none !important; }

html.dm-app-mode body :is(.cat-hero, .vac-hero, .res-hero, .listing-hero) {
  padding: 0 0 var(--s-4) !important;
  border-bottom: 0 !important;
  gap: 0 !important;
}

html.dm-app-mode body :is(.hero-title) {
  margin: 0 0 var(--s-2) !important;
  font: var(--w-bold) var(--t-large-title)/var(--t-large-title-lh) var(--f-display) !important;
  letter-spacing: -.022em !important;
  color: var(--c-label) !important;
  text-wrap: balance;
}
html.dm-app-mode .hero-title em { font-style: italic; font-weight: var(--w-regular); }

html.dm-app-mode body :is(.hero-sub) {
  margin: 0 !important;
  max-width: 38ch;
  font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui) !important;
  color: var(--c-label-2) !important;
}

/* Sentence case. `font-variant-caps` deja las versalitas verdaderas donde el
   diseño editorial las quiere (la marca las usa), pero el `text-transform`
   que gritaba «BUSCAR» y «FILTRAR» se apaga. */
html.dm-app-mode body :is(.hero-eyebrow, .sec-label, .fchip-label, .search-btn,
  .btn-navy, .btn-ghost, .btn-up, .btn-ver-vac, .empty-btn, .contact-btn, .save-btn,
  .dm-gs-eyebrow, .dm-gs-recent-title,
  .stpill, .vbadge, .page-count) {
  text-transform: none !important;
  letter-spacing: 0 !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   15 · Contrastes medidos que no llegaban
   ───────────────────────────────────────────────────────────────────────────
   Uno por uno, los once que salieron en la medición. Ninguno es un color
   nuevo: todos pasan al escalón de la escala semántica que sí cumple.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body :is(.nov-date, .page-count, .dm-gs-entry-cta-sub,
  .hero-pro-sub, .dm-miga-sep, .nov-all, .nov-desc, .psb-estado,
  .vtag, .job-meta, .card-meta, .meta, .muted) {
  color: var(--c-label-2) !important;   /* 7.39:1 en claro, 10.42:1 en oscuro */
}

html.dm-app-mode body :is(.vbadge.nueva) {
  color: var(--c-error) !important;      /* 5.07:1 — antes 4.26:1 */
  background: color-mix(in srgb, var(--c-error) 12%, transparent) !important;
}
html.dm-app-mode body :is(.stpill.live) {
  color: var(--c-exito) !important;      /* 6.93:1 — antes 4.26:1 */
  background: color-mix(in srgb, var(--c-exito) 12%, transparent) !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   16 · Se acabó el desbordamiento lateral
   ───────────────────────────────────────────────────────────────────────────
   La fila de filtros y el selector de orden de Vacantes se cortaban contra el
   borde derecho sin ninguna señal de que hubiera más. Ahora la fila se desliza
   de verdad —con su margen de seguridad y sin barra visible— y el contenido
   entra hasta el borde, que es lo que pide «Extend content to fill the screen»
   (Layout > Best practices).
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body :is(.filters-row, .vac-filters, .chips-row, .tabs-row,
  .filter-chips-group, .unified-row) {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--s-2);
  margin-inline: 0;
  padding-inline: var(--margen);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
html.dm-app-mode body :is(.filters-row, .vac-filters, .chips-row, .tabs-row,
  .filter-chips-group, .unified-row)::-webkit-scrollbar { display: none; }
html.dm-app-mode body :is(.filters-row, .vac-filters, .chips-row, .tabs-row,
  .filter-chips-group, .unified-row) > * { flex: 0 0 auto; scroll-snap-align: start; }

/* La etiqueta «FILTRAR:» y el desplegable de orden viajan con la fila, así que
   no pueden ser `flex: 0 0 auto` y quedarse fuera de la vista: la etiqueta se
   queda pegada al principio y el orden salta a su propia línea, donde se lee. */
html.dm-app-mode .fchip-label {
  position: sticky;
  left: 0;
  z-index: 1;
  padding-right: var(--s-2);
  background: linear-gradient(90deg, var(--c-bg) 76%, transparent);
  font: var(--w-medium) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui);
  color: var(--c-label-2);
  text-transform: none !important;
  letter-spacing: 0 !important;
}
html.dm-app-mode .unified-row { flex-wrap: wrap; }
html.dm-app-mode .unified-row > .sort-wrap,
html.dm-app-mode .unified-row > .sort-sel { margin-left: auto; }

/* ═══════════════════════════════════════════════════════════════════════════
   17 · Ventana ancha
   ───────────────────────────────────────────────────────────────────────────
   Una PWA instalada también corre en iPad y en escritorio. «Design for a
   full-screen view first, and only switch to a compact view when a version of
   the full layout no longer fits» (Layout > Tablet).
   ═══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 720px) {
  html.dm-app-mode { --margen: 24px; }
  html.dm-app-mode .dm-app-titulo-grande h1 { font-size: var(--t-large-title); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   18 · Adaptación del cristal al contenido
   ───────────────────────────────────────────────────────────────────────────
   «For smaller elements (toolbars, tab bars), the system adapts between light
   and dark appearance in response to underlying content» (Liquid Glass >
   Visual Properties). `app-mode.js` ya muestrea lo que hay debajo de la barra
   y escribe `data-glass-context`; lo que faltaba era que esos contextos
   estuvieran escritos en la escala semántica y no en literales claros, que es
   lo que dejaba la barra en hueso con el sistema en oscuro.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode #dm-app-bottom-nav[data-glass-context="dark"],
html.dm-app-mode #dm-app-header[data-glass-context="dark"] {
  /* Este velo era el color exacto de la página en oscuro: el dock quedaba
     invisible como superficie. Sobre contenido oscuro el cristal se aclara. */
  --m-veil: rgba(78, 77, 73, .50);
  --m-luz: 1.55;
  --m-brillo: rgba(255, 255, 255, .18);
  --m-borde: rgba(255, 254, 250, .12);
  --c-label: #F1F0EC;
  --c-label-3: #B6B4AC;
  --c-tinte: #DBD194;
  --c-relleno-2: rgba(241, 240, 236, .16);
}

/* Sobre una foto o un color fuerte el velo se cierra un poco más: es la
   regla de opacidad de la variante clara —«If underlying content is bright →
   add a dark dimming layer»— traducida a subir la opacidad del velo en vez de
   añadir una capa nueva. */
html.dm-app-mode #dm-app-bottom-nav[data-glass-context="photo"],
html.dm-app-mode #dm-app-bottom-nav[data-glass-context="color"],
html.dm-app-mode #dm-app-header[data-glass-context="photo"],
html.dm-app-mode #dm-app-header[data-glass-context="color"] {
  --m-veil: color-mix(in srgb, var(--c-bg) 86%, transparent);
  --m-sat: 1.5;
}

/* ═══════════════════════════════════════════════════════════════════════════
   19 · La lista de categorías
   ───────────────────────────────────────────────────────────────────────────
   Era ya lo más parecido a una app que tenía el producto: filas con su cuenta
   y su chevrón. Le faltaba el acabado —tarjeta agrupada, separadores metidos
   hasta el texto, 44 pt de alto— y le sobraba una cosa que no se ve en una
   captura: las categorías con cero perfiles se pueden tocar y llevan a una
   pantalla vacía. Se marcan y dejan de invitar.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode .cat-list-section { padding: 0 !important; }

html.dm-app-mode .cat-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  margin: var(--s-5) var(--margen) var(--s-2);
  padding: 0;
  border: 0;
}
html.dm-app-mode .cat-header-label {
  font: var(--w-semibold) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui);
  color: var(--c-label-2);
  text-transform: none;
  letter-spacing: 0;
}
html.dm-app-mode .cat-header-count {
  font: var(--w-regular) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui);
  color: var(--c-label-3);
}

html.dm-app-mode .cat-list {
  margin: 0 var(--margen) var(--s-6);
  border-radius: var(--r-3);
  background: var(--c-elevado);
  box-shadow: inset 0 0 0 .5px var(--c-separador);
  overflow: hidden;
}
html.dm-app-mode .cat-item {
  position: relative;
  min-height: var(--toque);
  padding: var(--s-3) var(--s-4);
  border: 0 !important;
  background: transparent;
  gap: var(--s-3);
}
html.dm-app-mode .cat-item + .cat-item::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--s-4);
  right: 0;
  height: .5px;
  background: var(--c-separador);
}
html.dm-app-mode .cat-item:active { background: var(--c-relleno); }
html.dm-app-mode .cat-item .cat-name,
html.dm-app-mode .cat-item .cat-title {
  font: var(--w-regular) var(--t-body)/var(--t-body-lh) var(--f-display);
  color: var(--c-label);
}
html.dm-app-mode .cat-item .cat-count,
html.dm-app-mode .cat-item .cat-meta {
  font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui);
  color: var(--c-label-3);
}

/* Una fila que no lleva a ninguna parte deja de parecer que lleva. El estado
   se dice con opacidad Y con el chevrón apagado, no sólo con el color:
   «Convey information with more than color alone» (Accessibility > Vision). */
html.dm-app-mode .cat-item[data-vacia="1"] { opacity: .55; }
html.dm-app-mode .cat-item[data-vacia="1"] .cat-arrow,
html.dm-app-mode .cat-item[data-vacia="1"] svg { visibility: hidden; }

/* ═══════════════════════════════════════════════════════════════════════════
   20 · Inicio
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode #page-inicio .hero-banner { min-height: 0; padding: var(--s-1) var(--margen) var(--s-3); }
html.dm-app-mode #page-inicio .main-inner { padding: 0 0 var(--s-7); }

html.dm-app-mode body :is(#page-inicio .hero-title-pro, #page-inicio .hero-banner h1, #page-inicio .hero-banner h2) {
  margin: 0 0 var(--s-1) !important;
  font: var(--w-bold) var(--t-large-title)/var(--t-large-title-lh) var(--f-display) !important;
  letter-spacing: -.022em !important;
  color: var(--c-label) !important;
}
html.dm-app-mode #page-inicio .hero-pro-sub {
  margin: 0 !important;
  font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui) !important;
  color: var(--c-label-2) !important;
}

/* Lo que antes eran cuatro accesos rápidos repetidos ahora es una división
   entre bloques. El ancla se queda en el DOM porque el orden de inserción
   cuelga de ella; no pinta nada. */
html.dm-app-mode .dm-app-anchor { display: none !important; }

html.dm-app-mode .dm-app-editorial-feature { margin: 0 var(--margen) var(--s-6); }
html.dm-app-mode .dm-app-editorial-feature strong {
  font: var(--w-semibold) var(--t-title-2)/var(--t-title-2-lh) var(--f-display);
}
html.dm-app-mode .dm-app-editorial-feature small {
  font: var(--w-regular) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui);
  color: rgba(255, 254, 250, .9);
}

/* ═══════════════════════════════════════════════════════════════════════════
   21 · El hueco de arriba
   ───────────────────────────────────────────────────────────────────────────
   Medido: en /vacantes el primer texto de la pantalla empezaba a 152 px, con
   la cabecera acabando en 51. Cien píxeles de nada en un teléfono de 844, y
   con la cabecera transparente son cien píxeles de nada literal.
   Vienen de `main.page { padding-top: 64px }` más `.vac-hero { padding-top:
   22px }`: dos aires pensados para una página web con menú encima.

   `body` ya reserva el alto de la cabecera. Lo que sobra es todo lo demás.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode main.page,
html.dm-app-mode main.page.active { padding-top: 0 !important; }

html.dm-app-mode body :is(.cat-hero, .vac-hero, .res-hero, .listing-hero) {
  padding-top: var(--s-2) !important;
  padding-left: var(--margen) !important;
  padding-right: var(--margen) !important;
}

/* `responsive-v2.css` mete 20 px a los lados de estos bloques con `!important`
   y la misma especificidad que esta hoja. Se resuelve cargando la capa de
   diseño al final del `<head>` —lo hace `app-mode.js`—, no subiendo aquí la
   especificidad regla a regla. Este bloque sólo fija el margen que queremos. */
html.dm-app-mode body :is(.cat-list-section, .profiles-section, .res-content,
  .vac-list-section, .jobs-section) {
  padding-left: 0 !important;
  padding-right: 0 !important;
  max-width: none !important;
}

html.dm-app-mode body :is(.unified-row) {
  padding-left: var(--margen) !important;
  padding-right: var(--margen) !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   22 · La fila de filtros
   ───────────────────────────────────────────────────────────────────────────
   Tenía tres cosas peleándose por un renglón de 390 px: la etiqueta, siete
   chips y el desplegable de orden. El resultado era «Medio tiemp» cortado
   contra el borde y «Más recientes» directamente fuera de la pantalla, sin
   nada que dijera que había más.

   Se separan en dos renglones —el orden arriba a la derecha, los chips abajo
   deslizándose— porque son dos decisiones distintas y competían por el mismo
   sitio. Los chips se deslizan de verdad, con el último asomando, que es lo
   que dice que hay más: «use progressive disclosure to help people discover
   content that's currently hidden» (Layout > Visual hierarchy).
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body :is(.unified-row) {
  display: flex !important;
  flex-direction: column !important;
  align-items: stretch !important;
  gap: var(--s-2) !important;
  padding: var(--s-2) 0 var(--s-3) !important;
  overflow: visible !important;
  border-bottom: .5px solid var(--c-separador) !important;
}

html.dm-app-mode body :is(.unified-row) > :is(.sort-wrap, .sort-sel) {
  order: -1;
  align-self: flex-end;
  margin: 0 var(--margen) !important;
  min-height: var(--toque);
  padding: 0 !important;
  color: var(--c-label-2) !important;
  font: var(--w-medium) var(--t-subhead)/1.2 var(--f-ui) !important;
}

html.dm-app-mode body :is(.filter-chips-group) {
  display: flex !important;
  flex-direction: row !important;
  flex-wrap: nowrap !important;
  align-items: center;
  gap: var(--s-2) !important;
  padding: 0 var(--margen) !important;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding-inline: var(--margen);
  scrollbar-width: none;
}
html.dm-app-mode body :is(.filter-chips-group)::-webkit-scrollbar { display: none; }
html.dm-app-mode body :is(.filter-chips-group) > * { flex: 0 0 auto; scroll-snap-align: start; }

/* Las dos páginas llaman distinto a la misma etiqueta: `.fchip-label` en
   Explorar y `.fchip-lbl` en Vacantes. */
html.dm-app-mode body :is(.fchip-label, .fchip-lbl) {
  position: sticky;
  left: 0;
  z-index: 1;
  align-self: center;
  padding-right: var(--s-2);
  background: linear-gradient(90deg, var(--c-bg) 74%, transparent);
  font: var(--w-medium) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui) !important;
  color: var(--c-label-2) !important;
  text-transform: none !important;
  letter-spacing: 0 !important;
}

/* Un chip mide 44 pt de alto y respira. El activo es tinta sólida: forma y
   color a la vez, no sólo color. */
html.dm-app-mode body :is(.fchip) {
  min-height: var(--toque);
  padding: 0 var(--s-4) !important;
  border-radius: var(--r-max) !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   23 · Los últimos rescoldos, uno a uno
   ───────────────────────────────────────────────────────────────────────────
   Nada de esto es una regla general: son exactamente los elementos que
   quedaron señalados al volver a medir. Escribirlos por su nombre cuesta más
   que un comodín, pero un comodín aplanaría los pesos y las tipografías que
   sí son decisiones.
   ═══════════════════════════════════════════════════════════════════════════ */

/* DM Sans se colaba todavía por los botones del panel y por un renglón del
   buscador guiado. Con esto la app se queda en las dos tipografías de la
   marca: Petrona y Inter. */
html.dm-app-mode body :is(.btn-navy, .btn-ghost, .save-btn, .contact-btn,
  .dm-gs-entry-cta-sub, .profile-visibility-toggle,
  /* Los tres botones de Inicio: la acción sugerida, sus secundarias y el
     «Ver más» del carril. Declaran Inter en su propia regla y aun así perdían:
     `dashboard-memberships-v2.css` trae
     `html[data-dm-page="dashboard"] button`, que con el atributo suma más
     especificidad que una clase y además se carga después. En el navegador se
     quedan en DM Sans como el resto del panel —es el diseño aprobado de esa
     pantalla—; dentro de la app mandan las dos tipografías de la marca. */
  .dm-nba-boton, .dm-nba-mas, .dm-di-todo) {
  font-family: var(--f-ui) !important;
}

/* Peso 300 en textos de 12 y 13 px. «In general, avoid light font weights…
   which can be difficult to see, especially when text is small»
   (Typography > Ensuring legibility). Suben al regular. */
html.dm-app-mode body :is(.cat-n, .vcol-pin, .vcol-txt, .vcol-modal,
  .bio-text, .empty-msg) {
  font-weight: var(--w-regular) !important;
}

/* Los títulos finos sí pueden seguir siendo finos —a 22 px un peso ligero se
   lee de sobra y es parte del tono editorial de la marca—, pero pasan al
   escalón de la escala en vez de quedarse en un tamaño suelto. */
html.dm-app-mode body :is(.page-title) {
  font: var(--w-regular) var(--t-title-2)/var(--t-title-2-lh) var(--f-display) !important;
  color: var(--c-label) !important;
}

/* «Ver todos» era un enlace de 67×20: se puede leer, pero acertarle con el
   pulgar en marcha es otra cosa. */
html.dm-app-mode body :is(.see-all) {
  display: inline-flex;
  align-items: center;
  min-height: var(--toque);
}

/* ═══════════════════════════════════════════════════════════════════════════
   24 · Borde de scroll
   ───────────────────────────────────────────────────────────────────────────
   «Background content is blurred and reduced in opacity at scroll edges to
   enhance legibility» (Liquid Glass > Visual Properties). En una página web no
   se puede desenfocar lo que hay fuera de un elemento, así que el degradado se
   pone delante: el contenido se desvanece hacia el fondo justo antes de llegar
   a la barra, en vez de meterse debajo y asomar entre las etiquetas.

   Es lo que separa una barra translúcida de una barra sucia.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body::after {
  content: "";
  position: fixed;
  z-index: var(--z-borde);
  inset: auto 0 0;
  height: calc(96px + var(--dm-app-safe-bottom, 0px));
  pointer-events: none;
  background: linear-gradient(to top, var(--c-bg) 26%, transparent);
}

html.dm-keyboard-open body::after { display: none; }

@media (prefers-reduced-transparency: reduce) {
  html.dm-app-mode body::after { display: none; }
}

/* Las últimas versalitas de la app: los rótulos de sección del panel. Van con
   tracking, que es lo que las hacía legibles a 11 px, pero siguen siendo texto
   de interfaz gritado. En caja baja se leen igual y no interrumpen. */
html.dm-app-mode body :is(.pm-titulo, .pm-periodo, .cfg-sec-ey, .sect-label,
  .cat-header-label, .dm-gs-recent-title, .dm-gs-eyebrow) {
  text-transform: none !important;
  letter-spacing: 0 !important;
  font: var(--w-semibold) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui) !important;
  color: var(--c-label-2) !important;
}
html.dm-app-mode body :is(.pm-periodo) { font-weight: var(--w-regular) !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   25 · Movimiento
   ───────────────────────────────────────────────────────────────────────────
   Las curvas de fábrica del navegador son flojas. `ease-out` de CSS arranca
   demasiado despacio para una pulsación, y `ease-in` —que arranca lento— hace
   que cualquier cosa parezca que va con retraso: no se usa en interfaz, nunca.

   Tres curvas, cada una con su trabajo:
     · salida  — algo que entra o sale de pantalla; arranca rápido, se posa
     · ambas   — algo que se mueve DENTRO de la pantalla
     · cajón   — la de los paneles que suben desde abajo (viene de Ionic)

   Y una regla que casi nadie aplica: **la salida va más rápida que la
   entrada**. Al abrir algo se está esperando; al cerrarlo ya se decidió, y
   cualquier milisegundo es tiempo perdido mirando algo que sobra.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode {
  --sal: cubic-bezier(.23, 1, .32, 1);
  --amb: cubic-bezier(.77, 0, .175, 1);
  --cajon: cubic-bezier(.32, .72, 0, 1);

  --t-tap: 140ms;      /* pulsación */
  --t-rapido: 180ms;   /* cerrar, desvanecer */
  --t-medio: 260ms;    /* abrir una hoja */
  --t-lento: 240ms;    /* transición entre pantallas: se ve decenas de veces al día */

  /* La hoja antigua usaba estas dos; se reapuntan a las nuevas curvas para
     que el armazón heredado se mueva igual que lo nuevo. */
  --dm-motion-curve: var(--sal);
  --dm-motion-instant: var(--t-tap);
  --dm-motion-fast: var(--t-rapido);
  --dm-motion-base: var(--t-medio);
}

/* Pulsación. 0.97 se siente; 0.90 se ve. Lo que se busca es que el dedo note
   que la interfaz lo oyó, no una animación. */
html.dm-app-mode .dm-app-pressable,
html.dm-app-mode body :is(button, a[href], [role="button"], .cat-item, .fchip,
  .dm-gs-entry-cta, .dm-app-sheet-action) {
  transition: transform var(--t-tap) var(--sal), background-color var(--t-tap) var(--sal), opacity var(--t-tap) var(--sal);
}
html.dm-app-mode .dm-app-pressable:active,
html.dm-app-mode body :is(button, a[href], [role="button"], .cat-item, .fchip,
  .dm-gs-entry-cta, .dm-app-sheet-action):active {
  transform: scale(.97);
  filter: none;
}
/* Una fila de lista no se encoge entera: se ilumina. Encoger un renglón de
   ancho completo mueve el texto de al lado y se nota como un tirón. */
html.dm-app-mode body :is(.cat-item, .dm-lista > *):active { transform: none; background: var(--c-relleno-2); }

/* El hover sólo donde hay un cursor de verdad. En una pantalla táctil el
   estado de hover se queda pegado después de tocar. */
@media (hover: hover) and (pointer: fine) {
  html.dm-app-mode body :is(.cat-item, .dm-lista > *, .dm-app-sheet-action):hover { background: var(--c-relleno); }
  html.dm-app-mode body :is(.dm-app-nav-item):hover { color: var(--c-label); }
}

/* La hoja entra con la curva del cajón y sale más rápido y más plana. */
html.dm-app-mode .dm-app-sheet { transition: transform var(--t-medio) var(--cajon); }
html.dm-app-mode .dm-app-sheet-layer:not(.is-open) .dm-app-sheet { transition-duration: var(--t-rapido); }
html.dm-app-mode .dm-app-sheet-backdrop { transition: opacity var(--t-medio) var(--sal); }
html.dm-app-mode .dm-app-sheet-layer:not(.is-open) .dm-app-sheet-backdrop { transition-duration: var(--t-rapido); }

/* Escalonado de la primera pantalla. 40 ms entre elementos: lo justo para que
   se lea como una secuencia y no como una espera. Nunca bloquea el toque —
   el estado final es el visible, y la animación sólo lo alcanza. */
@media (prefers-reduced-motion: no-preference) {
  html.dm-app-mode .cat-item,
  html.dm-app-mode .dm-gs-entry-cta {
    animation: dmEntrada var(--t-medio) var(--sal) both;
  }
  html.dm-app-mode .cat-item:nth-child(1) { animation-delay: 0ms; }
  html.dm-app-mode .cat-item:nth-child(2) { animation-delay: 40ms; }
  html.dm-app-mode .cat-item:nth-child(3) { animation-delay: 80ms; }
  html.dm-app-mode .cat-item:nth-child(4) { animation-delay: 120ms; }
  html.dm-app-mode .cat-item:nth-child(n + 5) { animation-delay: 160ms; }
}

/* Nada aparece desde la nada: se entra desde el 96 % y desde 6 px, no desde
   `scale(0)`. En el mundo real las cosas no nacen de un punto. */
@keyframes dmEntrada {
  from { opacity: 0; transform: translateY(6px) scale(.985); }
  to { opacity: 1; transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   26 · Capas con nombre
   ───────────────────────────────────────────────────────────────────────────
   La app tenía 8800, 9000, 11000 y 12000 escritos a mano en cuatro archivos.
   Números así no dicen nada sobre el orden que se pretendía y cada uno nuevo
   se elige subiendo un poco más que el anterior, hasta el 99999.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode {
  --z-fondo: 0;
  --z-esqueleto: 10;
  --z-borde: 20;      /* el degradado que funde el contenido con la barra */
  --z-barras: 30;     /* cabecera y barra de pestañas */
  --z-asistente: 40;  /* el buscador guiado a pantalla completa */
  --z-aviso: 50;      /* sin conexión */
  --z-hoja: 60;       /* hojas y diálogos */
  --z-transicion: 70; /* el relevo entre dos documentos: por encima de todo */
}
html.dm-app-mode .dm-app-skeleton-layer { z-index: var(--z-esqueleto); }
html.dm-app-mode body::after { z-index: var(--z-borde); }
html.dm-app-mode #dm-app-header,
html.dm-app-mode #dm-app-bottom-nav { z-index: var(--z-barras); }
html.dm-app-mode .dm-gs { z-index: var(--z-asistente); }
html.dm-app-mode .dm-app-offline-banner { z-index: var(--z-aviso); }
html.dm-app-mode .dm-app-sheet-layer { z-index: var(--z-hoja); }
html.dm-app-mode .dm-app-transicion { z-index: var(--z-transicion); }

/* La barra del editor de perfil es de la web, y trae su `z-index: 126` de
   antes de que esta escala existiera. Ningún ancestro suyo crea contexto de
   apilamiento, así que ese 126 compite EN LA RAÍZ contra los 60 de la hoja y
   los gana: abrir el menú dentro del editor pintaba la barra encima, tapando
   el asa, el botón de cerrar y la cabecera con quién eres.
   Y no se notaba tocando, porque `apagarElFondo()` pone `inert` en el fondo e
   `inert` quita del hit-test lo que sigue pintándose: los toques llegaban al
   menú y la pantalla decía otra cosa.
   Aquí es cromo de página, no una capa: va por debajo de las barras. Sigue
   por encima del contenido, que es lo que necesita cuando en pantalla ancha
   vuelve a ser `sticky`. */
html.dm-app-mode .edit-topbar { z-index: calc(var(--z-barras) - 1); }

/* ═══════════════════════════════════════════════════════════════════════════
   27 · Navegación fluida
   ───────────────────────────────────────────────────────────────────────────
   Inicio, Guardados y Perfil son la misma página: cambiar de pestaña entre
   ellas era instantáneo. Buscar y Vacantes son documentos distintos, y ahí la
   app se comportaba como una web: parpadeo en blanco, cabecera que se rehace,
   barra de pestañas que salta.

   Se arregla con la transición entre documentos del navegador, que es una
   regla de CSS. No hace falta un enrutador en el cliente, ni reescribir las
   páginas como una SPA, ni una dependencia: el navegador toma una foto de la
   pantalla que se va, otra de la que llega, y las cruza.

   Lo que hace que se sienta como una app y no como un crossfade es marcar la
   cabecera y la barra de pestañas con un nombre: al llamarse igual en las dos
   páginas, el navegador entiende que SON el mismo elemento y las deja
   quietas mientras el contenido cambia por debajo. Es exactamente lo que hace
   una barra de pestañas nativa.

   Donde no haya soporte, la navegación es la de siempre. No hay nada que
   degradar.
   ═══════════════════════════════════════════════════════════════════════════ */
@view-transition { navigation: auto; }

html.dm-app-mode #dm-app-header { view-transition-name: dm-barra-superior; }
html.dm-app-mode #dm-app-bottom-nav { view-transition-name: dm-barra-inferior; }

::view-transition-group(dm-barra-superior),
::view-transition-group(dm-barra-inferior) { animation-duration: 0ms; }

::view-transition-old(root) {
  animation: dmSaleVista var(--t-rapido, 180ms) cubic-bezier(.23, 1, .32, 1) both;
}
::view-transition-new(root) {
  animation: dmEntraVista var(--t-lento, 240ms) cubic-bezier(.23, 1, .32, 1) both;
}

@keyframes dmSaleVista { to { opacity: 0; transform: scale(.995); } }
@keyframes dmEntraVista { from { opacity: 0; transform: translateY(8px); } }

/* Con movimiento reducido la transición no se apaga: se vuelve un fundido
   corto. «Reduced motion means fewer and gentler animations, not zero», y
   además un salto seco entre pantallas es peor para orientarse. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: dmFunde 140ms linear both; }
  @keyframes dmFunde { from { opacity: 0; } to { opacity: 1; } }
  /* El escalonado se queda, pero sin subir: sólo aparece. Quitarlo del todo
     hace que una lista larga se materialice de golpe, que es más brusco. */
  html.dm-app-mode .cat-item,
  html.dm-app-mode .dm-gs-entry-cta { animation-name: dmAparece; }
}

@keyframes dmAparece { from { opacity: 0; } to { opacity: 1; } }

/* ═══════════════════════════════════════════════════════════════════════════
   28 · Texto heredado sobre superficies nuevas
   ───────────────────────────────────────────────────────────────────────────
   Poner la app en oscuro no es cambiar el fondo. Los componentes que traen su
   color escrito a mano —`#2B2B42` en el título del buscador guiado, en el
   contador de la lista de categorías, en el chevrón— siguieron siendo tinta
   sobre tinta: 1.33:1 donde hacen falta 4.5.

   Lo encontró la suite de navegador midiendo píxeles, no la lectura del CSS,
   porque en el CSS cada uno de esos colores es perfectamente razonable por
   separado. Se reapuntan al escalón semántico que les toca, y así valen para
   las dos apariencias sin duplicar nada.
   ═══════════════════════════════════════════════════════════════════════════ */
/* `--text-xsoft` es `#AAAAAA`: 2.30:1 sobre el papel, cuando el mínimo para
   texto pequeño es 4.5. No es un color de texto —es un color de filete— y sin
   embargo lo usan 132 declaraciones para etiquetas de verdad: «Los cambios
   principales se guardan solos», «Visibilidad en el directorio», «Tu plan
   actual», «Tu código de referido». Medido, era el 100 % de los 47 fallos de
   contraste de las secciones del panel.
  
   Se reapunta al escalón que sí es de texto: `--c-label-3`, 5.44:1 en claro y
   6.26:1 en oscuro. Una línea en vez de auditar 132 declaraciones, y lo que se
   escriba mañana con la misma variable nace legible.
  
   En oscuro estaba reapuntado a `--c-label-4`, que es 3.49:1: mejor que 2.30
   pero por debajo del mínimo igualmente. Ahora los dos van al mismo escalón. */
html.dm-app-mode { --text-xsoft: var(--c-label-3); }

html.dm-app-mode body :is(.dm-gs-entry-title, .dm-gs-entry-cta-label,
  .cat-name, .cat-title, .psb-name, .nov-title, .vcard-title, .job-title) {
  color: var(--c-label) !important;
}

html.dm-app-mode body :is(.dm-gs-entry-cta-sub, .cat-n, .cat-count, .cat-meta,
  .psb-cat, .nov-name, .vcard-meta, .job-meta) {
  color: var(--c-label-2) !important;
}

html.dm-app-mode body :is(.cat-arrow, .dm-gs-entry-cta-chevron, .chevron) {
  color: var(--c-label-3) !important;
}

/* Las superficies que la página pinta en blanco o en hueso: en oscuro serían
   parches encendidos en mitad de la pantalla. */
html.dm-app-mode body :is(.dm-gs-entry, .dm-gs-entry-cta, .psb, .cat-list,
  .vcard, .nov-card) {
  background: var(--c-elevado);
  color: var(--c-label);
}

@media (prefers-color-scheme: dark) {
  /* Y lo que la página deja explícitamente en blanco puro, que no es una
     superficie de esta app en ninguna apariencia. */
  html.dm-app-mode body :is([style*="background:#fff"], [style*="background: #fff"],
    [style*="background-color:#fff"], [style*="background-color: #fff"]) {
    background: var(--c-elevado) !important;
  }
  /* Y el beige al 24 % de las dos cajas de Ayuda («¿Primera vez por aquí?»):
     sobre casi negro se compone en un gris claro, y el texto callado encima
     medía 3.72:1. Oculto mientras la prueba atravesaba las capas
     translúcidas en vez de componerlas. */
  html.dm-app-mode body [style*="background:rgba(214,211,197"] {
    background: var(--c-relleno) !important;
  }
  html.dm-app-mode body :is(.hero-status) {
    background: var(--c-relleno-2) !important;
    color: var(--c-label) !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   29 · La paleta heredada, en oscuro
   ───────────────────────────────────────────────────────────────────────────
   Ir clase por clase arreglando textos oscuros sobre fondos oscuros no acaba
   nunca: siempre queda una. Pero el panel no escribe sus colores a mano — los
   declara como variables en `:root` y las usa 171 veces. Reapuntar esas
   variables lo cambia todo de una vez, y lo que se añada mañana usando las
   mismas variables nace ya funcionando en las dos apariencias.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (prefers-color-scheme: dark) {
  html.dm-app-mode {
    --bg: var(--c-bg);
    --text: var(--c-label);
    --navy: var(--c-label);
    --text-mid: var(--c-label-2);
    --text-soft: var(--c-label-3);
    --text-xsoft: var(--c-label-3);
    --border: var(--c-separador);
    --border-mid: var(--c-separador);
    --stone: var(--c-relleno-3);
    --stone-mid: var(--c-relleno-3);
    --stone-light: var(--c-relleno);

    /* Los colores de estado bajan de saturación y suben de luz: los tonos
       pensados para leerse sobre hueso se apagan sobre un fondo casi negro. */
    --green: #7FC9A0;
    --green-bg: rgba(127, 201, 160, .14);
    --amber-c: #E5B65C;
    --amber-bg: rgba(229, 182, 92, .14);
    /* #B42318 sobre #14141C es 2.4:1: el mensaje de error del formulario de
       tarjeta se leería peor que el resto del texto justo cuando más falta
       hace leerlo. */
    --red-c: #F28B82;
    /* El ámbar de `--amber-c` está pensado para leerse sobre hueso; sobre el
       fondo oscuro es al revés, así que `--amber-ink` sube aquí igual que él. */
    --amber-ink: #E5B65C;
    --c-olive: var(--c-tinte);
    --c-olive-bg: rgba(219, 209, 148, .14);

    /* Explorar y Vacantes declaran su propia paleta con los mismos nombres
       más algunos suyos. `--bg-mid` es el que pintaba la tarjeta de vacante:
       con el texto ya claro y la tarjeta todavía en hueso, salía 1.78:1. */
    --bg-mid: var(--c-bg-2);
    --card-border: var(--c-separador);

    /* El blog, el artículo, las novedades y los tres legales cargan
       `blog.css`, que declara SU propia paleta en `:root` con otros nombres.
       Por eso quedaron fuera de todo esto y en oscuro eran lo que eran:
       medido, 172 textos por debajo del contraste y 127 superficies casi
       blancas sólo en Novedades; 294 y 150 entre las seis.
    
       `--paper` tiene los dos oficios de `--navy`: es el fondo de la página y
       es la tinta encima de un botón navy. Reapuntarlo funciona en los dos
       porque los dos giran a la vez —navy se aclara, paper se oscurece— y el
       par sigue leyéndose. */
    --paper: var(--c-bg);
    --paper-mid: var(--c-bg-2);
    --ink: var(--c-label);
    --soft: var(--c-label-3);
    --line: var(--c-separador);
    --white: var(--c-elevado);
    --dm-text-secondary: var(--c-label-2);
    --dm-text-muted: var(--c-label-3);
    --em-dark: #F08A72;
    --em-light: rgba(240, 138, 114, .18);
    --em-txt: #F5B8A8;
    --fl-dark: #D8A8C4;
    --fl-light: rgba(216, 168, 196, .18);
    --fl-txt: #E8CEDD;
    --pr-dark: var(--c-tinte);
    --pr-light: rgba(219, 209, 148, .16);
    --green-light: rgba(127, 201, 160, .18);
    --hover-bg: var(--c-relleno-2);
  }

  /* El único color escrito a mano que queda en Vacantes. */
  html.dm-app-mode body :is(.vtag-modo) {
    background: var(--c-relleno-2) !important;
    color: var(--c-label-2) !important;
  }

  /* Y los que quedan escritos a mano en el blog. Reapuntar la paleta arregló
     294 textos y 150 superficies de golpe; estos no llevaban variable ninguna
     —`#fff`, `#3e3b39`, `#4a4638`— así que hay que nombrarlos. Son pocos y
     conocidos, que es la misma razón por la que arriba se nombran los fondos
     navy en vez de renunciar al remapeo. */
  html.dm-app-mode body :is(.lead-article, .blog-search, .share-panel,
    .editorial-note, .related-section, .post-card, .featured-card) {
    background: var(--c-elevado) !important;
  }
  /* El cuerpo del artículo: 18 px en Petrona sobre casi negro, escrito
     `#3e3b39`, daba 1.65:1. Es el texto que la gente viene a leer. */
  html.dm-app-mode body .article-body p,
  html.dm-app-mode body .article-body li { color: var(--c-label) !important; }
  html.dm-app-mode body :is(.author-avatar, .lead-tag, .article-category) {
    background: var(--c-relleno-2) !important;
    color: var(--c-label) !important;
  }
  html.dm-app-mode body .blog-search span { color: var(--c-label-2) !important; }

  /* Las secciones del panel que traen superficies claras escritas a mano: la
     barra del editor (`rgba(255,254,250,.97)` de 390×280), su navegación, y
     el buscador de ayuda con sus botones. Y los textos que van encima. */
  html.dm-app-mode body :is(.edit-topbar, .edit-nav, .help-search,
    .help-search button, .help-card, .cfg-card) {
    background: var(--c-elevado) !important;
  }
  html.dm-app-mode body :is(.edit-topbar-title, .edit-topbar-sub, .save-status,
    .edit-back, .cfg-btn-edit) { color: var(--c-label) !important; }
  html.dm-app-mode body :is(.edit-topbar-sub, .save-status) { color: var(--c-label-2) !important; }
  /* La banda morada de referidos escribe encima con opacidades bajas del mismo
     lila; sobre el ciruela eso era 1.01 y 1.20:1. */
  html.dm-app-mode body :is(.ref-eyebrow, .ref-band strong,
    .ref-ejemplo strong, .ref-ej-card strong) {
    color: rgba(238, 214, 250, .96) !important;
  }
  /* «Plan actual» y el botón de buscar en Ayuda: pastillas con fondo claro
     escrito a mano y tinta clara encima. Se les da el par entero. */
  html.dm-app-mode body :is(.plan-current-badge, .help-search button) {
    background: var(--c-relleno-2) !important;
    color: var(--c-label) !important;
  }
  html.dm-app-mode body :is(.help-card a, .help-links a, .help-grid a, .help-grid > *) {
    background: var(--c-elevado) !important;
  }
  /* El botón de soporte lleva su fondo navy escrito en el atributo `style`, así
     que no lo alcanza la lista de fondos navy del final de este archivo y era
     el único bloque claro de 203×46 que quedaba sobre casi negro. Se apunta por
     lo que ES —el `mailto:` de la pantalla de ayuda— y no por su estilo, que es
     lo que se querría cambiar y no se puede desde aquí. */
  html.dm-app-mode body #page-ayuda a[href^="mailto:"] {
    background: var(--c-tinte) !important;
    color: var(--c-bg) !important;
  }
  /* El círculo «Tú» del ejemplo de referidos: fondo navy con tinta blanca.
     Con navy aclarado, blanco sobre blanco. Se le da el par entero, como a los
     demás fondos navy de la lista del final. */
  html.dm-app-mode body .ref-ej-you {
    background: var(--c-tinte) !important;
    color: var(--c-bg) !important;
  }
  /* Los últimos grises escritos a mano: `#4e4b49` en la entradilla del blog y
     en el cuerpo de la política editorial, y el texto del bloque de llamada,
     que va sobre navy y con navy ya aclarado se quedó sin fondo que lo
     sostenga. */
  html.dm-app-mode body :is(.lead-excerpt, .editorial-page > p,
    .editorial-page section p, .editorial-page li) { color: var(--c-label) !important; }
  /* El bloque de llamada del artículo se queda OLIVA, que es lo que decide la
     lista de fondos navy del final de este archivo —y esa gana, porque va
     después—. Aquí sólo se arregla lo de dentro: sobre oliva, la tinta es
     oscura. Poner el gris claro del sistema encima daba 1.08:1, que es
     literalmente no verse. */
  html.dm-app-mode body :is(.article-cta p, .article-cta-kicker,
    .article-cta h2, .article-cta strong) { color: inherit !important; }
  html.dm-app-mode body .article-cta-secondary {
    border-color: rgba(18, 18, 16, .32) !important;
    color: var(--c-bg) !important;
  }
  /* El perfil público: las etiquetas y el retrato traen blanco propio, y las
     dos cajas que envuelven la página siguen en hueso. */
  html.dm-app-mode body :is(.profile-tags .tag, .tags-row .tag) {
    background: var(--c-relleno-2) !important;
    color: var(--c-label) !important;
    border-color: var(--c-separador) !important;
  }
  html.dm-app-mode body :is(.stat-recom-label, .stat-con-label) { color: var(--c-label-2) !important; }
  html.dm-app-mode body :is(.browser-wrap, .site) { background: var(--c-bg) !important; }
  /* La tarjeta de contacto y la fila de pestañas del perfil traen `#FFFEFA`
     escrito: dos superficies casi blancas de 350×167 y 390×49 sobre casi
     negro, y con ellas todo lo que llevan dentro. */
  html.dm-app-mode body :is(.contact-card, .tabs-scroll-wrap) {
    background: var(--c-elevado) !important;
  }
  /* Y el nombre y la ciudad van en `#111` fijo: tinta negra sobre casi negro,
     1.03:1. Es el nombre de la persona, lo primero que se lee de un perfil. */
  html.dm-app-mode body .profile-name { color: var(--c-label) !important; }
  html.dm-app-mode body .profile-city { color: var(--c-label-2) !important; }
  html.dm-app-mode body .profile-avatar { background: var(--c-relleno-2) !important; }
  /* Los 404 y 500 escriben su paleta entera en línea, en su propio `<style>`:
     no cargan `blog.css` ni el sistema del panel, así que no hay variable que
     reapuntar. */
  html.dm-app-mode body :is(.code, .wrap h1, .wrap h1 em) { color: var(--c-label) !important; }
  html.dm-app-mode body .wrap p { color: var(--c-label-2) !important; }
  html.dm-app-mode body .ghost {
    background: var(--c-relleno) !important;
    border-color: var(--c-separador) !important;
    color: var(--c-label) !important;
  }
  /* La acción principal del blog conserva su énfasis con el acento, igual que
     `.btn-navy` y compañía: un botón claro de 316×46 sobre fondo oscuro
     deslumbra, y es exactamente lo que la lista de arriba viene evitando. */
  html.dm-app-mode body :is(.read-button) {
    background: var(--c-tinte) !important;
    color: var(--c-bg) !important;
  }

  /* `--navy` tiene dos oficios: es la tinta en 147 sitios y el fondo en 53.
     Reapuntarla sirve para la tinta y rompería los fondos, así que esos —que
     son una lista corta y conocida— se resuelven aquí. La acción principal
     conserva su énfasis con el acento; lo demás pasa a relleno. */
  html.dm-app-mode body :is(.btn-navy, .contact-btn, .article-cta, .mobile-create,
    .btn-ver-vac, .search-btn, .pm-btn-primary, .plan-cta, .btn-upgrade,
    /* `.vcard-btn` —«Ver vacante →» de las guardadas— faltaba de esta lista.
       Es `background: var(--navy)` como las demás, así que en oscuro salía
       #FFFEFA: un bloque casi blanco de 320×44 sobre pantalla oscura, que es
       justo lo que esta regla viene evitando. No se veía porque la prueba
       fallaba antes por otra cosa —las iniciales a 1,35:1— y no llegaba a
       mirar las superficies. */
    .vcard-btn,
    /* `.dm-mp-add` —«Añadir método» del método de pago— por lo mismo: medido
       en oscuro daba 1.00, tinta #FFFEFA sobre fondo #FFFEFA. */
    .dm-mp-add,
    /* «Completar» de Inicio pinta `--navy` con tinta `--bg`, así que no se
       rompía: se INVERTÍA. En oscuro salía una pastilla blanca, lo más
       brillante de la pantalla y la única acción principal que no era oro.
       «Copiar» de Referidos, igual: la acción de esa pantalla. */
    .dm-nba-boton, .ref-copy-btn,
    /* El botón principal de las hojas (guardar, enviar la solicitud de verificación) era `background: var(--navy)`:
       en oscuro, una pastilla blanca de 346×48 —lo más brillante de la hoja y la única acción que no era oro. */
    .dm-disp-guardar) {
    background: var(--c-tinte) !important;
    color: var(--c-bg) !important;
  }
  /* Al copiar, «Copiar» se pone verde: es la confirmación, no se pierde. */
  html.dm-app-mode body .ref-copy-btn.copied { background: var(--green) !important; }
  /* La pestaña activa del editor hacía lo mismo —pastilla blanca de 109×48
     junto a «Guardar cambios»—, pero no es una acción: es dónde estás. Lleva
     relleno, como el segmento elegido de un control del sistema. */
  html.dm-app-mode body .edit-nav-item.active {
    background: var(--c-relleno-3) !important;
    color: var(--c-label) !important;
  }
  html.dm-app-mode body :is(.av-circle, .av-ini, .btn-up, .dm-social-icon,
    .cfg-slider, .mini-switch, .pg-btn.on) {
    background: var(--c-relleno-3) !important;
    color: var(--c-label) !important;
  }
}

/* Vacantes pinta cada fila con `.vrow:hover { background: #fff }`, sin la
   media query que distingue un cursor de un dedo. En una pantalla táctil ese
   estado se queda pegado después de tocar, y en oscuro es un fogonazo blanco
   en mitad de la lista. Aquí la fila usa la superficie que le toca, y el
   realce del hover se hace con relleno, que funciona en las dos apariencias. */
html.dm-app-mode body :is(.vrow) {
  background: var(--c-elevado);
  border-bottom-color: var(--c-separador);
  border-radius: var(--r-3);
}
html.dm-app-mode body :is(.vrow):active { background: var(--c-relleno); }

@media (hover: hover) and (pointer: fine) {
  html.dm-app-mode body :is(.vrow):hover { background: var(--c-relleno); }
}

/* El logotipo es un PNG de tinta sólida. En oscuro se quedaba casi invisible
   sobre la barra. `brightness(0) invert(1)` lo pasa a hueso sin necesidad de
   un segundo archivo — es el mismo truco que ya usa la pantalla de acceso. */
@media (prefers-color-scheme: dark) {
  html.dm-app-mode .dm-app-header-brand { filter: brightness(0) invert(1); opacity: .92; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   30 · Superficies que gritan en oscuro
   ───────────────────────────────────────────────────────────────────────────
   Estas dos pasaban la prueba de contraste con nota —texto oscuro sobre
   blanco se lee perfectamente— y aun así estaban mal: un bloque blanco puro
   en mitad de una pantalla oscura deslumbra y rompe la jerarquía. El contraste
   mide si algo se lee, no si algo debería estar ahí.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (prefers-color-scheme: dark) {
  html.dm-app-mode body :is(.search-bar, .search-input, .search-loc, .tags-field,
    .tag-suggestions, .form-textarea, .job-apply-card, .job-apply-input,
    /* Los campos de las hojas (completar el perfil, disponibilidad, pedir la verificación): `.form-input` pinta
       `#fffefb` escrito a mano, y sobre la hoja oscura era un rectángulo casi blanco de 346×120. */
    .dm-hoja-campo .form-input, .dm-disp-fecha .form-input,
    .notif-card, .plan-info-box, .edit-item, .edit-empty, .galeria-slot, .logros-edit) {
    background: var(--c-elevado) !important;
    color: var(--c-label) !important;
    border-color: var(--c-separador) !important;
  }
  html.dm-app-mode body :is(.search-sep) { background: var(--c-separador) !important; }

  /* La etiqueta del año de un trabajo llevaba `color: #111` escrito a mano y
     un fondo de papel: sobre la tarjeta oscura daba 1.03:1, o sea invisible.
     No se veía en las pruebas porque ningún perfil de la base de demostración
     tenía trabajos publicados; en cuanto uno los tuvo, apareció. */
  html.dm-app-mode body :is(.tags-row .tag, .project-tag, .profile-tag-soft) {
    background: var(--c-relleno-2) !important;
    border-color: var(--c-separador) !important;
    color: var(--c-label) !important;
  }

  /* ── Las cuatro insignias de tipo de perfil ──────────────────────────────
     `profile-type-colors.css` define cuatro paletas —freelance ámbar, empresa
     índigo, proveedor violeta, explorador pizarra— con un solo juego de
     valores, pensados para papel. Medida la insignia «EMPRESA» en el perfil
     público en oscuro:

       fondo #EBF1FF contra la página #14141C ····· 16.19:1
       texto #1A2A52 contra ese fondo ············· 12.40:1

     La segunda cifra es la que miran las pruebas de contraste, y está
     perfecta. Por eso nunca saltó. Pero la primera dice lo que de verdad
     pasa: una pastilla casi blanca que es lo más brillante de la pantalla,
     más que el nombre de la persona, para decir «empresa». Es exactamente lo
     que abre esta sección: el contraste mide si algo se lee, no si algo
     debería estar ahí.

     Se corrigen las CUATRO paletas y no la insignia, porque las mismas
     variables visten también las tarjetas del directorio, el panel y la ficha
     de una vacante. Medido, cada una: texto sobre su fondo 9.00, 9.06, 10.12
     y 10.91:1 —muy por encima del mínimo— y el fondo contra la página entre
     1.16 y 1.31:1, que es una superficie tranquila en vez de un foco. */
  html.dm-app-mode body {
    --profile-freelance-bg:   #3A2617;
    --profile-freelance-dot:  #FF9155;
    --profile-freelance-text: #F0C5A3;

    --profile-empresa-bg:   #1E2742;
    --profile-empresa-dot:  #7A99EC;
    --profile-empresa-text: #BCCAF5;

    --profile-proveedor-bg:   #2E1A3A;
    --profile-proveedor-dot:  #BE7FE0;
    --profile-proveedor-text: #DFC5F2;

    --profile-explorador-bg:   #2C2B28;
    --profile-explorador-dot:  #BDBBB3;
    --profile-explorador-text: #E6E3DA;
  }
  /* «PLUS» va en una pastilla blanca al 72 % con la tinta heredada: en claro
     es tinta oscura sobre blanco, pero aquí la tinta de la insignia es clara
     y quedaba claro sobre casi blanco —1.3:1 medido en el perfil público de
     una empresa Plus: no se leía—. En oscuro la pastilla es un velo claro. */
  html.dm-app-mode body .dm-profile-type-badge .pro-tag {
    background: rgba(255, 255, 255, .14);
  }

  /* El chip activo en claro es tinta sobre hueso; invertido sería hueso sobre
     tinta, o sea una pastilla blanca encendida. Un gris elevado dice
     «seleccionado» igual de claro sin ser lo más brillante de la pantalla. */
  html.dm-app-mode body :is(.fchip.on, .chip.on, .dm-gs-chip[aria-pressed="true"]) {
    background: var(--c-bg-3);
    border-color: var(--c-label-4);
    color: var(--c-label);
  }
}

@media (prefers-color-scheme: dark) {
  /* «Guardar vacante» es una acción secundaria: en claro es un botón blanco
     con borde, y en oscuro tiene que seguir siendo el discreto de los dos. */
  html.dm-app-mode body :is(.btn-save-job, .save-btn) {
    background: var(--c-relleno-2) !important;
    color: var(--c-label) !important;
    border-color: var(--c-separador) !important;
  }
  /* El hueco de una imagen que todavía no llegó. Un rectángulo gris claro
     esperando en una pantalla oscura parece un fallo de carga. */
  html.dm-app-mode body :is(.nov-img, .img-placeholder) {
    background: var(--c-bg-3) !important;
  }
  /* Las iniciales que hacen de imagen mientras no hay foto llevan su gris
     escrito a mano (#555): sobre el hueco oscuro daban 1.95:1. */
  html.dm-app-mode body :is(.nov-img, .img-placeholder) > * {
    color: var(--c-label-3) !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   31 · La barra es el buscador
   ───────────────────────────────────────────────────────────────────────────
   Una pastilla con el menú a la izquierda, la búsqueda en medio y el avatar a
   la derecha. Lo que se usa a diario cabe en una fila y no hay que bajar a
   buscarlo — que es justo lo que pasaba con las dos tarjetas de «¿talento o
   producto?» a media pantalla de distancia.

   El arranque: la marca aparece centrada y, un segundo después, se retira
   hacia arriba mientras entra lo que la pastilla hace. Se anuncia quién eres
   una vez y luego se dice para qué sirve, que es lo que hace falta saber a
   partir de la segunda vez. Sólo en el arranque en frío: `sessionStorage`
   recuerda que ya saludó, así que cambiar de pestaña no lo repite.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode #dm-app-header { display: block; }

/* La fila. Sólo coloca: no tiene fondo, ni borde, ni sombra, ni radio.
   Tenía los tres —`border-radius: var(--r-max)`, `background: var(--c-relleno)`
   y un `box-shadow` interior— y eso era la cápsula que envolvía el menú, la
   marca y el «+». Lo que se quiere son tres piezas flotando: el cristal es de
   los dos controles y la marca va directa sobre el fondo de la pantalla. */
html.dm-app-mode .dm-app-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

html.dm-app-mode .dm-app-bar-btn {
  width: var(--toque);
  height: var(--toque);
  padding: 0;
  border: 0;
  border-radius: var(--r-max);
  background: transparent;
  color: var(--c-label-2);
  display: grid;
  place-items: center;
  cursor: pointer;
}
html.dm-app-mode .dm-app-bar-btn svg {
  width: 22px; height: 22px;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
html.dm-app-mode .dm-app-bar-btn:active { background: var(--c-relleno-2); }

/* El centro: la marca, quieta.
   Era un botón que se convertía en «Buscar en Design Match» a los 1.1 s. La
   búsqueda se fue a Buscar, que es donde se usa, y con ella se fue el motivo
   de que la marca se apartara. Una cabecera que cambia de contenido sola, en
   todas las pantallas, es ruido: aquí ya no cambia. */
/* Centrada sobre la PANTALLA, no sobre el hueco que dejan los botones.
   `#dm-app-header` está fijo, así que es el bloque contenedor: `left: 50%`
   se resuelve sobre su caja de relleno, que es el ancho completo. Así el
   centro óptico no se mueve aunque el «+» no esté, aunque los botones cambien
   de tamaño o aunque el área segura sea distinta a cada lado —en horizontal,
   con la muesca a la izquierda, un centrado por columnas se corre varios
   píxeles y se nota—.
   Fuera de flujo, además, la fila sólo tiene que colocar dos botones a los
   extremos, que es exactamente lo que hace `space-between`. */
html.dm-app-mode .dm-app-bar-marca {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: auto;
  pointer-events: none;
}

@media (prefers-color-scheme: dark) {
  html.dm-app-mode .dm-app-bar-marca { filter: brightness(0) invert(1); opacity: .92; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   31 ter · Los dos controles flotantes
   ───────────────────────────────────────────────────────────────────────────
   El menú y el «+» son hermanos: misma medida, mismo radio, mismo material.
   Y son lo ÚNICO de la cabecera que tiene material — la marca va limpia sobre
   el fondo, y el `<header>` no pinta nada en ningún estado.

   El brillo de arriba va en el propio `background`, en la misma pila que el
   velo, y no en un `::before`. Un pseudoelemento encima de una superficie con
   `backdrop-filter` es una capa compuesta más por control y por fotograma, y
   aquí no compra nada que un degradado no dé.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode .dm-app-control {
  border-radius: var(--r-max);
  color: var(--c-label);
  /* El velo, aligerado. `--m-veil` está calibrado para una barra de ancho
     completo (.88): en una pastilla pequeña ese blanco tapa el fondo y el
     control se ve lechoso en vez de traslúcido. */
  background:
    linear-gradient(180deg, var(--m-brillo-ctrl), rgba(255, 255, 255, 0) 56%),
    color-mix(in srgb, var(--m-veil) 72%, transparent);
  -webkit-backdrop-filter: blur(var(--m-blur-ctrl)) saturate(var(--m-sat)) brightness(var(--m-luz));
  backdrop-filter: blur(var(--m-blur-ctrl)) saturate(var(--m-sat)) brightness(var(--m-luz));
  box-shadow:
    inset 0 0 0 .5px var(--m-borde),
    inset 0 .5px 0 0 var(--m-brillo-ctrl),
    0 1px 2px rgba(20, 20, 30, .05),
    0 6px 18px rgba(20, 20, 30, .07);
}
/* La pulsación la da la escala que ya trae `.dm-app-pressable`. Esto sólo
   impide que el `background: var(--c-relleno-2)` de un botón normal borre el
   material al pulsar: el cristal se cierra un poco, no desaparece. */
html.dm-app-mode .dm-app-bar-btn.dm-app-control:active {
  background:
    linear-gradient(180deg, var(--m-brillo-ctrl), rgba(255, 255, 255, 0) 56%),
    color-mix(in srgb, var(--m-veil) 88%, transparent);
}

/* La entrada al buscador, dentro de Buscar.
   Es lo que era la pastilla de la cabecera, en el sitio donde de verdad se
   usa: al principio de la pantalla que busca perfiles. Con la misma forma de
   campo de búsqueda del sistema —redonda, con la lupa a la izquierda— para
   que se lea como lo que es aunque sea un botón y no un `<input>`: al tocarla
   se abre el buscador de pantalla completa, y un campo que no acepta el
   teclado donde se toca sería una promesa rota. */
html.dm-app-mode .dm-app-buscar-pastilla {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  width: 100%;
  min-height: var(--toque);
  /* Pegada al titular que la explica y con aire hacia la lista, no al revés.
     Con 22 px abajo la primera categoría empezaba en y=231 de 844: se comió
     los ocho píxeles que costaba entrar en el presupuesto de la pantalla. */
  margin: 0 0 16px;
  padding: 0 14px;
  border: 0;
  border-radius: var(--r-max);
  background: var(--c-relleno);
  box-shadow: inset 0 0 0 .5px var(--m-borde);
  color: var(--c-label-3);
  font: var(--w-regular) var(--t-callout)/1.2 var(--f-ui);
  text-align: left;
  cursor: pointer;
}
html.dm-app-mode .dm-app-buscar-pastilla:active { background: var(--c-relleno-2); }
html.dm-app-mode .dm-app-buscar-pastilla svg {
  width: 18px; height: 18px; flex: 0 0 auto;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
/* Con la letra al 200 % el rótulo mide más que la pastilla: se corta con
   puntos suspensivos en vez de desbordarse, como hacía el de la cabecera. */
html.dm-app-mode .dm-app-buscar-pastilla span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════════════════════
   31 bis · El «+» y su menú
   ───────────────────────────────────────────────────────────────────────────
   «The menu appears near the control that reveals it» (Menus). El gesto
   empieza arriba a la derecha, así que la respuesta sale de ahí: el menú se
   ancla bajo el botón, alineado a su borde derecho, y crece desde esa esquina.
   Una hoja subiendo desde abajo respondería a un gesto que nadie hizo.

   Nunca desde `scale(0)`: nada aparece de la nada. Empieza al 94 % con la
   opacidad a cero y sube ocho píxeles, que es lo que hace que parezca salir
   del botón y no encenderse encima de él.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode .dm-app-bar-mas svg { stroke-width: 2; }

html.dm-app-mode .dm-app-mas-capa {
  position: fixed;
  inset: 0;
  z-index: var(--z-hoja);
}

/* Mientras el menú está abierto, la cabecera va por encima de él: el «+» no
   puede quedar debajo de la capa que él mismo abrió. El menú empieza ocho
   píxeles por debajo de la barra, así que subirla no tapa nada. */
html.dm-app-mode.dm-app-mas-abierto #dm-app-header { z-index: calc(var(--z-hoja) + 1); }

/* Y mientras se va, deja de estorbar. La capa tarda 180 ms en irse, y durante
   ese rato seguía recogiendo todos los toques: quien elegía una opción y
   tocaba algo enseguida se encontraba con que el primer toque no hacía nada. */
html.dm-app-mode .dm-app-mas-capa:not(.esta-abierto) { pointer-events: none; }

/* El fondo no se pinta: sólo recoge el toque de fuera. Oscurecer la pantalla
   por un menú de cuatro renglones sería tratarlo como un diálogo, y no lo es
   — no interrumpe nada, se quita solo. */
html.dm-app-mode .dm-app-mas-fondo {
  position: absolute;
  inset: 0;
  width: 100%;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: default;
  -webkit-appearance: none;
  appearance: none;
}

html.dm-app-mode .dm-app-mas-menu {
  position: fixed;
  min-width: 216px;
  /* En 320 px la cabecera deja 8 px a cada lado: el menú nunca puede pasar de
     ahí, y con el rótulo más largo («Subir a la galería») no llega. */
  max-width: min(264px, calc(100vw - 16px));
  padding: 6px;
  border-radius: 14px;
  /* Opaco, y con el mismo tono que las hojas. `--c-relleno` es un relleno
     traslúcido —vale para un botón sobre la barra, no para una capa que tapa
     contenido—: con él se leía la pantalla de debajo a través del menú. */
  background: var(--c-elevado);
  box-shadow:
    inset 0 0 0 .5px var(--m-borde),
    0 12px 32px rgba(0, 0, 0, .18),
    0 2px 8px rgba(0, 0, 0, .10);
  transform-origin: top right;
  transform: scale(.94) translateY(-8px);
  opacity: 0;
  transition:
    opacity var(--t-rapido) var(--sal),
    transform var(--t-rapido) var(--sal);
}
html.dm-app-mode .dm-app-mas-capa.esta-abierto .dm-app-mas-menu {
  transform: none;
  opacity: 1;
}

html.dm-app-mode .dm-app-mas-opcion {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  width: 100%;
  /* 44 pt de alto útil: es un menú que se toca con el pulgar en marcha. */
  min-height: var(--toque);
  padding: 0 10px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--c-label);
  font: var(--w-regular) var(--t-callout)/1.25 var(--f-ui);
  text-align: left;
  cursor: pointer;
}
html.dm-app-mode .dm-app-mas-opcion:active { background: var(--c-relleno-2); }

html.dm-app-mode .dm-app-mas-glifo {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  color: var(--c-label-2);
}
html.dm-app-mode .dm-app-mas-glifo svg {
  width: 20px; height: 20px;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}

@media (prefers-reduced-motion: reduce) {
  /* Menos movimiento no es ninguno: el menú sigue apareciendo, pero sin
     recorrer nada. Quitarle también el fundido lo dejaría parpadeando. */
  html.dm-app-mode .dm-app-mas-menu {
    transform: none;
    transition: opacity 120ms linear;
  }
}

/* En una ficha, la barra vuelve a ser lo que tiene que ser allí. */
html.dm-app-mode .dm-app-bar.is-detalle {
  /* La fila base pasó a `flex` para las tres piezas sueltas; aquí sí hay tres
     columnas de verdad —volver, título, compartir— y el título tiene que
     ocupar el centro exacto. */
  display: grid;
  background: transparent;
  box-shadow: none;
  grid-template-columns: var(--toque) minmax(0, 1fr) var(--toque);
}
html.dm-app-mode .dm-app-bar.is-detalle .dm-app-header-title { text-align: center; }

/* La caja del directorio se convierte en un disparador: se ve igual, pero
   lleva al buscador de arriba en vez de abrir un segundo. */
html.dm-app-mode .dm-app-search-proxy { cursor: pointer; }
html.dm-app-mode .dm-app-search-proxy input { pointer-events: none; }

/* ═══════════════════════════════════════════════════════════════════════════
   32 · El panel de búsqueda
   ───────────────────────────────────────────────────────────────────────────
   Pantalla completa, campo arriba y resultados debajo según se escribe. El
   conmutador «Talento / Producto» aparece cuando hay algo escrito: preguntarlo
   antes obliga a clasificar el problema antes de poder contarlo, que es
   exactamente lo que hacían las dos tarjetas.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-bus-open, html.dm-bus-open body { overflow: hidden; }

html.dm-app-mode .dm-bus,
.dm-bus {
  position: fixed;
  z-index: var(--z-hoja, 60);
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--c-bg, #FFFEFA);
  opacity: 0;
  transition: opacity var(--t-rapido, 180ms) var(--sal, ease-out);
}
html.dm-app-mode .dm-bus.is-open,
.dm-bus.is-open { opacity: 1; }

.dm-bus-bar {
  display: flex;
  align-items: center;
  gap: var(--s-1, 4px);
  padding: calc(var(--s-2, 8px) + var(--dm-app-safe-top, 0px)) max(var(--s-2, 8px), var(--dm-app-safe-right, 0px)) var(--s-2, 8px) max(var(--s-2, 8px), var(--dm-app-safe-left, 0px));
  border-bottom: .5px solid var(--c-separador, rgba(43, 43, 66, .13));
}

.dm-bus-icono {
  width: var(--toque, 44px);
  height: var(--toque, 44px);
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  border-radius: var(--r-max, 999px);
  background: transparent;
  color: var(--c-label-2);
  display: grid;
  place-items: center;
  cursor: pointer;
}
.dm-bus-icono svg {
  width: 22px; height: 22px;
  fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}
.dm-bus-icono:active { background: var(--c-relleno, rgba(43, 43, 66, .06)); }

.dm-bus-campo {
  flex: 1;
  min-width: 0;
  min-height: var(--toque, 44px);
  border: 0;
  background: none;
  padding: 0 var(--s-1, 4px);
  /* 16 px es el suelo real: por debajo, Safari hace zoom al enfocar y deja la
     pantalla descuadrada justo cuando se empieza a escribir. */
  font: var(--w-regular, 400) max(var(--t-callout, 16px), 16px)/1.2 var(--f-ui, Inter, sans-serif);
  color: var(--c-label, #2B2B42);
}
.dm-bus-campo:focus { outline: none; }
.dm-bus-campo::-webkit-search-cancel-button { display: none; }

.dm-bus-tipos {
  display: flex;
  gap: var(--s-2, 8px);
  padding: var(--s-3, 12px) var(--margen, 16px) 0;
}
.dm-bus-tipos[hidden] { display: none; }
/* 44 y no 36: es la medida de un dedo, y estos dos deciden qué se busca. */
.dm-bus-tipos button {
  min-height: var(--toque, 44px);
  padding: 0 var(--s-4, 16px);
  border: .5px solid var(--c-separador, rgba(43, 43, 66, .13));
  border-radius: var(--r-max, 999px);
  background: transparent;
  color: var(--c-label-2);
  font: var(--w-medium, 500) var(--t-subhead, 15px)/1 var(--f-ui, Inter, sans-serif);
  cursor: pointer;
}
.dm-bus-tipos button[aria-pressed="true"] {
  background: var(--c-label, #2B2B42);
  border-color: var(--c-label, #2B2B42);
  color: var(--c-bg, #FFFEFA);
}
@media (prefers-color-scheme: dark) {
  .dm-bus-tipos button[aria-pressed="true"] {
    background: var(--c-bg-3);
    border-color: var(--c-label-4);
    color: var(--c-label);
  }
}

.dm-bus-cuerpo {
  flex: 1;
  min-height: 0;
  padding: var(--s-3, 12px) var(--margen, 16px) calc(var(--s-8, 44px) + var(--dm-app-safe-bottom, 0px));
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.dm-bus-titulo {
  margin: var(--s-2, 8px) 0 var(--s-2, 8px);
  font: var(--w-semibold, 600) var(--t-footnote, 13px)/1.3 var(--f-ui, Inter, sans-serif);
  color: var(--c-label-2);
}
/* Cuando no se entendió el término, la lista no es una respuesta a él. Es
   mejor decirlo que enseñar treinta perfiles como si lo fueran. */
.dm-bus-aviso {
  margin: 0 0 var(--s-4, 16px);
  padding: var(--s-3, 12px) var(--s-4, 16px);
  border-radius: var(--r-2, 12px);
  background: var(--c-relleno, rgba(43, 43, 66, .06));
  font: var(--w-regular, 400) var(--t-footnote, 13px)/1.5 var(--f-ui, Inter, sans-serif);
  color: var(--c-label-2);
  text-wrap: pretty;
}
.dm-bus-aviso b { font-weight: var(--w-semibold, 600); color: var(--c-label); }
.dm-bus-enlace {
  padding: 0;
  border: 0;
  background: none;
  color: var(--c-tinte);
  font: inherit;
  font-weight: var(--w-medium, 500);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.dm-bus-cuenta {
  margin: 0 0 var(--s-3, 12px);
  font: var(--w-regular, 400) var(--t-subhead, 15px)/1.3 var(--f-ui, Inter, sans-serif);
  color: var(--c-label-2);
}
.dm-bus-pista {
  margin: var(--s-2, 8px) 0 var(--s-5, 20px);
  max-width: 40ch;
  font: var(--w-regular, 400) var(--t-subhead, 15px)/1.45 var(--f-ui, Inter, sans-serif);
  color: var(--c-label-2);
}

.dm-bus-lista { display: grid; margin: 0 0 var(--s-5, 20px); }
.dm-bus-fila {
  display: flex;
  align-items: center;
  gap: var(--s-3, 12px);
  min-height: var(--toque, 44px);
  padding: var(--s-2, 8px) 0;
  border: 0;
  background: none;
  color: var(--c-label, #2B2B42);
  text-align: left;
  cursor: pointer;
}
.dm-bus-fila svg {
  width: 19px; height: 19px; flex: 0 0 auto;
  fill: none; stroke: var(--c-label-3); stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.dm-bus-fila > span {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: var(--w-regular, 400) var(--t-body, 17px)/1.3 var(--f-ui, Inter, sans-serif);
}
.dm-bus-fila > small {
  flex: 0 0 auto;
  font: var(--w-regular, 400) var(--t-footnote, 13px)/1 var(--f-ui, Inter, sans-serif);
  color: var(--c-label-3);
}
.dm-bus-fila:active { background: var(--c-relleno, rgba(43, 43, 66, .06)); }

.dm-bus-secundario {
  display: grid;
  gap: 2px;
  width: 100%;
  min-height: var(--toque, 44px);
  margin: var(--s-2, 8px) 0 0;
  padding: var(--s-3, 12px) var(--s-4, 16px);
  border: 0;
  border-radius: var(--r-2, 12px);
  background: var(--c-relleno, rgba(43, 43, 66, .06));
  color: var(--c-label, #2B2B42);
  text-align: left;
  font: var(--w-medium, 500) var(--t-body, 17px)/1.3 var(--f-ui, Inter, sans-serif);
  cursor: pointer;
}
.dm-bus-secundario small {
  font: var(--w-regular, 400) var(--t-footnote, 13px)/1.3 var(--f-ui, Inter, sans-serif);
  color: var(--c-label-2);
}
.dm-bus-secundario:active { background: var(--c-relleno-2, rgba(43, 43, 66, .1)); }

.dm-bus-vacio { padding: var(--s-6, 24px) 0; }

/* Las tarjetas son las del asistente, tal cual: mismo motor, mismo score,
   mismo dibujo. Aquí sólo se les da la rejilla que cabe en este panel. */
.dm-bus .dm-gs-results { display: grid; gap: var(--s-3, 12px); }

@media (prefers-reduced-motion: reduce) {
  html.dm-app-mode .dm-bus,
  .dm-bus { transition: none; opacity: 1; }
}

/* ── El anillo de foco en oscuro ──────────────────────────────────────────
   `dm-accessibility.css` dibuja el foco con un trazo oscuro y un halo claro
   para que se vea sobre cualquier superficie, y lo protege con `!important`
   porque una página puede decidir cómo es su botón pero no puede dejar sin
   rastro a quien navega con teclado. Es la decisión correcta y no se toca.

   Lo que sí faltaba: ese archivo invierte los dos colores sobre superficies
   marcadas como oscuras (`.dm-bg-dark`, `[data-theme="dark"]`…), pero no
   conoce `prefers-color-scheme`, que es la vía por la que esta app se pone
   oscura. Sin esto, el anillo era tinta sobre casi negro con un halo hueso
   de 5 px: se leía como un recuadro blanco alrededor del campo.

   Se invierten los dos, exactamente como hace ese archivo en su propio caso. */
@media (prefers-color-scheme: dark) {
  html.dm-app-mode :is(a, button, input, select, textarea, summary, [tabindex],
    [role="button"], [role="link"], [role="tab"], [role="menuitem"]):focus-visible {
    outline-color: var(--c-label) !important;
    box-shadow: 0 0 0 5px var(--c-bg);
  }
}

/* Y la barra entera acusa el foco, para que se note de dónde viene el anillo. */
html.dm-app-mode .dm-bus-bar:focus-within,
.dm-bus-bar:focus-within { border-bottom-color: var(--c-tinte); border-bottom-width: 2px; }

/* ═══════════════════════════════════════════════════════════════════════════
   33 · El acceso en oscuro
   ───────────────────────────────────────────────────────────────────────────
   Esto se me escapó entero, y por una razón concreta: la suite de navegador
   entra con sesión, así que nunca vio la pantalla de acceso. Medido con la
   sesión borrada y el sistema en oscuro:

     · «¿Olvidaste tu contraseña?»   1.63:1   (mínimo 4.5)
     · «Iniciar sesión»              3.00:1
     · «Continuar con Passkey»       1.00:1   — invisible, literalmente
     · «Crear cuenta»                1.63:1

   La causa: la tarjeta del acceso trae su fondo hueso escrito a mano en
   `app-mode.css`, mientras el modo oscuro reapuntaba los textos y los botones
   a los tonos de fondo oscuro. Texto de pantalla oscura sobre tarjeta clara.

   La tarjeta pasa a la superficie elevada de la apariencia, y los tres
   colores que la habitan se recalculan para ella. El fondo de la pantalla es
   el de la apariencia, con el arte de la marca encima (§51).
   ═══════════════════════════════════════════════════════════════════════════ */
@media (prefers-color-scheme: dark) {
  html.dm-app-mode .dm-app-auth-view {
    background: var(--c-elevado);
    box-shadow: 0 18px 60px rgba(0, 0, 0, .5);
  }
  html.dm-app-mode .dm-app-auth-view h1 { color: var(--c-label); }
  html.dm-app-mode .dm-app-auth-copy,
  html.dm-app-mode .dm-app-auth-switch { color: var(--c-label-2); }
  html.dm-app-mode .dm-app-auth-divider { color: var(--c-label-2); }
  html.dm-app-mode .dm-app-auth-divider::before,
  html.dm-app-mode .dm-app-auth-divider::after { background: var(--c-separador); }
  html.dm-app-mode .dm-app-check { color: var(--c-label-2); }
  html.dm-app-mode .dm-app-check a { color: var(--c-tinte); }

  /* Los campos: relleno y borde de la apariencia, no el hueso de la web. */
  html.dm-app-mode #dm-app-auth-shell .dm-app-field input,
  html.dm-app-mode #dm-app-auth-shell .dm-app-field select {
    background: var(--c-relleno);
    border-color: var(--c-separador);
    color: var(--c-label);
  }
  html.dm-app-mode #dm-app-auth-shell .dm-app-field > label { color: var(--c-label-2); }
  html.dm-app-mode #dm-app-auth-shell .dm-app-field:focus-within > label { color: var(--c-tinte); }
  html.dm-app-mode .dm-app-password-toggle { color: var(--c-label-2); }

  /* El vino de marca (#4C072D) sobre una tarjeta oscura da 3:1 con el texto
     en hueso encima. El oro es el acento de esta apariencia y con tinta
     encima da 11.1:1, así que la acción principal pasa a oro con tinta. */
  html.dm-app-mode #dm-app-auth-shell .dm-app-primary {
    background: var(--c-tinte);
    color: var(--c-bg);
  }
  /* Y la secundaria deja de ser tinta sobre tinta: relleno con borde. */
  html.dm-app-mode #dm-app-auth-shell .dm-app-secondary {
    background: var(--c-relleno-2);
    color: var(--c-label);
    box-shadow: inset 0 0 0 1px var(--c-separador);
  }
  html.dm-app-mode .dm-app-auth-link { color: var(--c-tinte); }
  html.dm-app-mode .dm-app-inline-error { color: var(--c-error); }
  html.dm-app-mode .dm-app-inline-success { color: var(--c-exito); }
  html.dm-app-mode .dm-app-auth-trust { color: var(--c-label-3); }
}

/* La fila de consentimiento del registro: una casilla de 20 px y dos enlaces
   legales de 15 px de alto dentro de una frase. Los tres por debajo del
   mínimo, y los tres imposibles de agrandar sin romper el renglón.

   Se agranda el área, no el dibujo. Los dos enlaces están uno al lado del
   otro, así que crecer sólo en vertical no los solapa entre sí — solaparlos
   provocaría toques en el enlace equivocado, que es peor que el problema. */
html.dm-app-mode .dm-app-check { position: relative; }
html.dm-app-mode .dm-app-check a,
html.dm-app-mode .dm-app-check input[type="checkbox"] { position: relative; }
html.dm-app-mode .dm-app-check a::after,
html.dm-app-mode .dm-app-check input[type="checkbox"]::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, var(--toque));
  height: var(--toque);
  transform: translate(-50%, -50%);
}

/* ═══════════════════════════════════════════════════════════════════════════
   34 · Cuando no se pudo saber
   ───────────────────────────────────────────────────────────────────────────
   Un error de red no es una sesión cerrada. Mandar al login a alguien que sí
   tenía sesión, porque el servidor tardó, le hace escribir su contraseña por
   un fallo que no es suyo — y si el servidor sigue caído, tampoco podrá.

   Así que hay un cuarto estado con su propia pantalla: dice lo que pasó y
   ofrece lo único que sirve. Ni logo infinito, ni login inventado.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode .dm-app-arranque-error {
  position: fixed;
  z-index: var(--z-hoja);
  inset: 0;
  display: grid;
  align-content: center;
  justify-items: center;
  gap: var(--s-3);
  padding: var(--s-6) max(var(--s-6), var(--dm-app-safe-left, 0px)) calc(var(--s-6) + var(--dm-app-safe-bottom, 0px)) max(var(--s-6), var(--dm-app-safe-right, 0px));
  background: var(--c-bg);
  text-align: center;
}
html.dm-app-mode .dm-app-arranque-iso {
  width: 56px;
  height: auto;
  margin-bottom: var(--s-2);
  opacity: .9;
}
@media (prefers-color-scheme: dark) {
  html.dm-app-mode .dm-app-arranque-iso { filter: brightness(0) invert(1); }
}
html.dm-app-mode .dm-app-arranque-error h1 {
  margin: 0;
  max-width: 18ch;
  font: var(--w-semibold) var(--t-title-2)/var(--t-title-2-lh) var(--f-display);
  letter-spacing: -.016em;
  color: var(--c-label);
  text-wrap: balance;
}
html.dm-app-mode .dm-app-arranque-error p {
  margin: 0 0 var(--s-3);
  max-width: 30ch;
  font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui);
  color: var(--c-label-2);
}
html.dm-app-mode .dm-app-arranque-error button { min-width: 13rem; }

/* ═══════════════════════════════════════════════════════════════════════════
   35 · El inicio aparece de una vez, no en dos olas
   ───────────────────────────────────────────────────────────────────────────
   Medido: el carrusel y la pieza editorial se pintaban 92 ms antes que el
   saludo con el nombre, porque los monta `app-mode.js` en cuanto hay armazón
   mientras el saludo espera a `/users/me`. No es contenido falso —es
   contenido de verdad— pero llega en dos tiempos, y eso se lee como una
   pantalla que se está montando delante de ti.

   `data-state="resolviendo"` es el único momento en que el panel admite que
   todavía no sabe de quién es la cuenta. Mientras dure, el inicio no se
   enseña; cuando se sabe, entra entero. No se retrasa nada: se deja de
   enseñar a medias. */
html.dm-app-mode #app[data-state="resolviendo"] #page-inicio {
  opacity: 0;
}
html.dm-app-mode #page-inicio {
  opacity: 1;
  transition: opacity var(--t-rapido) var(--sal);
}

@media (prefers-reduced-motion: reduce) {
  html.dm-app-mode #page-inicio { transition-duration: 100ms; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   36 · El perfil, como un perfil
   ───────────────────────────────────────────────────────────────────────────
   Medido en la app, antes de esto:

     .stat-recom       322 × 68 px, fondo #f0ede4
     .stat-conexiones  322 × 60 px, fondo #f0ede4
     .save-btn         322 × 46 px
     .contact-btn      322 × 46 px

   Cuatro bloques a todo el ancho, uno debajo de otro: 260 px de pantalla —un
   tercio del alto del teléfono— para dos cifras que valen cero y dos botones.
   La identidad, que es lo que alguien viene a ver, quedaba arrinconada arriba
   y el contenido de verdad empezaba fuera de pantalla.

   El fallo no era de la cabecera sino del ancho: `responsive-v2.css` fuerza
   `display:flex; flex-direction:column !important` sobre `.contact-card` en
   pantallas estrechas, y eso ganaba a la rejilla de dos columnas que
   `app-mode.css` pedía. Se apilaba todo.

   La forma correcta ya existe y todo el mundo la conoce: retrato a un lado,
   cifras en fila a su lado, identidad debajo y las dos acciones compartiendo
   una sola línea. Cabe en una pantalla y las pestañas se ven sin bajar.

   `display:contents` en la tarjeta de contacto es lo que lo hace posible sin
   tocar el HTML: sus cuatro hijos pasan a ser elementos de la cabecera y se
   colocan por `order`. El orden de lectura no se altera —los dos botones
   conservan el suyo—, así que quien navega con teclado o lector recorre lo
   mismo que ve.

   Sólo la app instalada. En escritorio la tarjeta sigue siendo una tarjeta.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .profile-header {
  display: grid !important;
  /* El retrato manda en la primera columna y ocupa dos filas; las cifras se
     reparten las otras dos, debajo del nombre. */
  /* 84 y no 78. Medido a 390 px: el nombre con su insignia mide 53 y las
     cifras 39; con el hueco de fila son 104 contra un retrato de 78, así que
     las cifras sobresalían 13 px por debajo y el retrato dejaba de anclar sus
     dos filas —que es justo lo que esta rejilla viene a hacer—. Con 84 y el
     hueco de fila más corto, la segunda fila cierra dentro. */
  grid-template-columns: 84px 1fr 1fr !important;
  align-items: center !important;
  column-gap: var(--s-4) !important;
  row-gap: var(--s-2) !important;
  padding: var(--s-5) var(--margen) var(--s-4) !important;
}

/* Las dos cajas que trae el HTML compartido desaparecen COMO cajas: sus hijos
   suben a la rejilla de la cabecera y se colocan uno a uno. Es lo que permite
   esta forma sin tocar un HTML que también usa el escritorio. */
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) :is(.profile-info, .contact-card) {
  display: contents !important;
}

/* Por defecto todo ocupa el ancho entero y cae en su propia fila, en el orden
   en que está escrito. Sólo cuatro piezas se colocan a mano. */
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .profile-header > *:not(.profile-avatar),
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .profile-info > *:not(.dm-perfil-titular),
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .contact-card > *:not(.stat-recom):not(.stat-conexiones) {
  grid-column: 1 / -1;
  min-width: 0;
  margin: 0 !important;
}

/* El perfil público traía `margin: 0 20px` de su hoja: la cabecera quedaba
   40 px más estrecha que la del panel y la rejilla se apretaba. Aquí la
   sangría la pone el `padding` de la propia cabecera, como en el panel. */
html.dm-app-mode body .profile-page .profile-header { margin-inline: 0 !important; }

/* Las iniciales, cuando no hay foto. El panel las escribía en un `style=` del
   JS y el público las dejaba en 17/400 alineadas a la izquierda dentro de un
   círculo: la letra caía descentrada. Una sola regla para los dos. */
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .profile-avatar {
  font: var(--w-semibold) var(--t-title-3)/1 var(--f-ui) !important;
  text-align: center !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
}

html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .profile-avatar {
  grid-area: 1 / 1 / 3 / 2;
  /* En píxeles y no en `rem`: es un dibujo, no texto. Si el cuerpo crece al
     200 % lo que se estira son las filas, no el retrato. */
  width: 84px !important;
  height: 84px !important;
  min-width: 84px !important;
  margin: 0 !important;
}

/* ── El nombre y su insignia, en una línea ─────────────────────────────────
   Juntos y pequeños, encima de las cifras. El nombre venía de
   `clamp(25px, 8vw, 32px)!important` —31.2 px en un teléfono de 390— que en
   una cabecera donde todo lo demás mide 13 es un titular de portada. */
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .dm-perfil-titular {
  grid-area: 1 / 2 / 2 / 4;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  min-width: 0;
}
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .profile-name {
  margin: 0 !important;
  font: var(--w-semibold) var(--t-callout)/1.2 var(--f-display) !important;
  letter-spacing: -.012em;
}
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .profile-badge {
  margin: 0 !important;
  padding: 2px var(--s-3) !important;
  /* 12 y no 11: `--t-caption-2` da 10.999 px con la raíz al 106.25 %, y el
     mínimo del sistema son 11. Un píxel de redondeo es igual de ilegible. */
  font-size: var(--t-caption-1) !important;
  /* El peso también: el público lo traía en 500 y el panel en 400, y una
     insignia de 12 px en medio pesa distinto en cada pantalla. */
  font-weight: var(--w-regular) !important;
  line-height: 1.5 !important;
}

/* ── Las cifras ────────────────────────────────────────────────────────────
   Sin caja, sin versalitas y sin espaciado de letra: eran dos tarjetas de
   panel de control puestas en una pantalla que es un perfil. */
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .stat-recom,
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .stat-conexiones {
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
  justify-content: center !important;
  /* Un solo píxel: la cifra y lo que cuenta son una pareja, no dos líneas
     que comparten columna. Con cuatro se leían separadas. */
  gap: 1px !important;
  min-height: 0 !important;
  min-width: 0;
  padding: 0 !important;
  margin: 0 !important;
  border: 0 !important;
  border-radius: 0 !important;
  background: none !important;
  text-align: center;
  /* La caja hereda el tamaño para que lo que no lleve regla propia —un punto,
     un separador— mida lo mismo en las dos pantallas. */
  font-size: var(--t-body) !important;
}
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .stat-recom { grid-area: 2 / 2 / 3 / 3; }
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .stat-conexiones { grid-area: 2 / 3 / 3 / 4; }

/* 17 px sobre 13, no 28 sobre 12.
   Medido en la referencia: la cifra es 1.3 veces la etiqueta. Aquí eran 2.5,
   y un «0» de 28 px en serifa deja de leerse como número —parece una letra
   suelta— mientras la etiqueta que lo explica se encoge debajo. */
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .stat-recom-number,
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .stat-con-number {
  font: var(--w-semibold) var(--t-body)/1.15 var(--f-display) !important;
  letter-spacing: -.01em;
  /* Cifras de ancho fijo: sin esto, pasar de 9 a 10 mueve la etiqueta. */
  font-variant-numeric: tabular-nums;
  color: var(--c-label);
}
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .stat-recom-label,
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .stat-con-label {
  margin: 0 !important;
  font: var(--w-regular) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui) !important;
  letter-spacing: 0 !important;
  text-transform: none !important;
  color: var(--c-label-2) !important;
}
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .stat-con-dot { display: none !important; }

/* ── Lo que va debajo, a todo el ancho ─────────────────────────────────────
   La descripción breve es un campo aparte de «Sobre mí» —el editor las guarda
   por separado— así que enseñarla aquí no duplica nada: es la línea que dice
   quién es alguien antes de que nadie abra una pestaña. Estaba oculta. */
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .profile-bio {
  display: block !important;
  font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui);
  color: var(--c-label-2);
}

/* ── Las dos acciones, en una sola línea ───────────────────────────────────
   Conservan el orden en que están escritas, así que el foco del teclado va
   por donde va la vista.

   Esto es lo único de la 36 que NO se amplía al perfil público, y es a
   propósito: el panel tiene dos acciones y el perfil ajeno cinco —guardar,
   compartir, reportar, contactar—. `flex: 1 1 0` reparte el ancho entre dos;
   aplicado a cinco controles de tamaños distintos deja una fila que no es la
   de nadie. Medido: al ampliarlo, la fila del perfil ajeno pasó a tener tres
   radios distintos, porque `:is(#page-mi-perfil, …)` vale lo que su argumento
   más específico —un id— y ganaba a la 45, que es de clases. Igualarlas sería
   quitarle funciones a una pantalla para que se parezca a la otra. */
html.dm-app-mode body #page-mi-perfil .dm-perfil-acciones {
  display: flex;
  gap: var(--s-3);
  margin-top: var(--s-2) !important;
}
html.dm-app-mode body #page-mi-perfil .save-btn,
html.dm-app-mode body #page-mi-perfil .contact-btn {
  box-sizing: border-box;
  flex: 1 1 0;
  width: auto !important;
  min-width: 0;
  min-height: var(--toque);
  margin: 0 !important;
  border-radius: 14px;
  font: var(--w-medium) var(--t-subhead)/1 var(--f-ui);
  letter-spacing: 0;
  text-transform: none;
}
/* Los dos con el mismo borde, aunque uno sea invisible.
   Con `flex-basis: 0` y `border-box`, una caja no puede medir menos que sus
   propios bordes y rellenos: el que llevaba borde partía de 26 px y el otro
   de 24, y el reparto del hueco libre los dejaba a 174 contra 172. Medido. */
html.dm-app-mode body #page-mi-perfil .contact-btn {
  border: 1px solid transparent !important;
}

/* ── El color de la tinta ──────────────────────────────────────────────────
   La vista previa del perfil escribe `#111` en veintitantos sitios. En la web
   es correcto —allí no hay modo oscuro— pero en la app, con el sistema en
   oscuro, eso es tinta sobre tinta. Medido: 1.03:1 en el nombre, la ciudad,
   la descripción y la tabla de información. Se re-apunta a la escala
   semántica, que ya sabe invertirse.

   Sólo lo que va sobre el fondo de la página. Lo que vive dentro de una
   pastilla con su propio color —etiquetas, insignias, el botón de editar— se
   queda como está: cambiarle la tinta rompería el par que ya funciona. */
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) :is(
  .profile-name, .stat-recom-number, .stat-con-number, .section-title,
  .service-title, .project-name, .red-platform, .tab-btn.active,
  .logros-table th, .logros-table td
) {
  color: var(--c-label) !important;
}
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) :is(
  .profile-city, .profile-bio, .bio-text, .info-label, .info-value,
  .service-desc, .project-sub, .red-handle, .tab-btn
) {
  color: var(--c-label-2) !important;
}
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .tab-btn.active { color: var(--c-label) !important; }

/* ── Las pestañas, escritas una vez para las dos pantallas ─────────────────
   El panel las tenía a 12 px con 2 de hueco (dashboard.html) y el público a
   15 con 8 (sección 45). Dos escalas para la misma fila, en la misma app.

   Los `!important` no son adorno: `responsive-v2.css` fija `font-size:12px
   !important` y `padding-inline:0 !important` sobre estos dos selectores —dos
   veces cada uno, a 1024 y a 600— de cuando la fila iba encajada entre
   flechas. Preguntado al navegador, no deducido de la hoja. */
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .tab-btn {
  font: var(--w-regular) var(--t-caption-1)/1 var(--f-ui) !important;
  /* Con `!important` por lo mismo: la entreletra de la web (.1em) ganaba y
     «Sobre mí» salía espaciado. Medido en el navegador: 1.2 px. */
  letter-spacing: -.005em !important;
  text-transform: none;
  padding-inline: var(--s-3) !important;
}
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .tab-btn.active {
  font-weight: var(--w-semibold);
}
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .tabs-row {
  gap: var(--s-1);
  padding-inline: var(--margen) !important;
  scroll-padding-inline: var(--margen);
  scrollbar-width: none;
}
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .tabs-row::-webkit-scrollbar { display: none; }

/* El verde de «Perfil publicado» era un literal claro: 2.72:1 en oscuro. */
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .dm-app-profile-status { color: var(--c-exito) !important; }
html.dm-app-mode body :is(#page-mi-perfil, .profile-page) .dm-app-profile-status > span { background: var(--c-exito) !important; }

/* ── Las flechas de las pestañas ───────────────────────────────────────────
   Dos botones flotando sobre los extremos de la fila de pestañas. En un ratón
   son un atajo; en un dedo son una trampa: la flecha derecha se pinta ENCIMA
   de la última pestaña visible, así que tocar «Logros» desplazaba la fila en
   vez de abrirla. Salió al medir —el navegador se negó a pulsar la pestaña
   porque «tabs-scroll-next intercepta el puntero»— y le pasa igual a un dedo.

   En una pantalla táctil la fila ya se arrastra sola. Las flechas sobran.

   ── Y faltaba la pantalla donde más se nota ──────────────────────────────
   Esto estaba escrito sólo para `#page-mi-perfil` y la vista previa, o sea
   para el perfil visto DESDE EL PANEL. El perfil público —`/perfil/:slug`,
   que es el que ve todo el mundo y el que se comparte— usa el mismo armazón
   de pestañas y se quedó con las flechas.

   El editor tiene la misma fila y se quedó igual: en oscuro la flecha de la
   derecha era un cuadro hueso de 36 px encima del navy.

   Medido ahí a 390 px, y es peor que una molestia: las dos flechas se quedan
   36 px cada una PASE LO QUE PASE, así que la fila se queda con 330 de los
   390. Su contenido mide 355. O sea que el desbordamiento que las flechas
   vienen a resolver LO CAUSAN ELLAS: con el ancho entero, las pestañas caben
   y no hay nada que desplazar. */
html.dm-app-mode body :is(#page-mi-perfil, #page-editar-perfil, .dm-profile-preview, .profile-page) .tabs-scroll-arrow {
  display: none !important;
}
/* Sin flechas, los dos carriles de 36 px que se les reservaban son sangría
   muerta: la fila de pestañas se queda con el ancho entero.

   Y el fondo del envoltorio estaba escrito en claro —`#fffefa`— así que en
   oscuro era una banda color hueso con las pestañas en blanco encima: 1.00:1
   medido en la pestaña activa. Era el fondo, no la tinta. */
html.dm-app-mode body :is(#page-mi-perfil, #page-editar-perfil, .dm-profile-preview, .profile-page) .tabs-scroll-wrap {
  grid-template-columns: 0 minmax(0, 1fr) 0 !important;
  background: var(--c-bg) !important;
  border-bottom-color: var(--c-separador) !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   37 · La portada del acceso
   ───────────────────────────────────────────────────────────────────────────
   Abrir la app sin sesión era encontrarse un formulario: correo, contraseña,
   «¿olvidaste tu contraseña?», iniciar sesión, un separador, Passkey, una
   línea de confianza y un pie con «crear cuenta». Ocho decisiones antes de
   saludar, y la primera pantalla del producto pidiendo en vez de ofrecer.

   La forma que usan las apps a las que se entra a diario —un banco, el correo
   del teléfono— es la contraria: un saludo, UNA acción y un par de cosas que
   se pueden hacer sin cuenta. El formulario no desaparece; llega cuando se
   pide, que es cuando alguien ya decidió entrar.

   Tres decisiones que no son de estilo:

   · El saludo va por hora del día y sólo lleva nombre si ya estaba en memoria
     de esta sesión. Guardarlo en el aparato para poder saludar días después
     sería dejar escrito quién usa este teléfono a cambio de una cortesía.
   · La acción principal es la Passkey SÓLO si este aparato registró una.
     Ofrecer biometría a quien nunca la activó termina en un diálogo del
     sistema que dice que no hay nada, en la primera pantalla del producto.
   · Los tres atajos llevan a sitios que funcionan sin sesión. Un atajo que
     pide iniciar sesión al tocarlo no es un atajo, es un rodeo.
   ═══════════════════════════════════════════════════════════════════════════ */

/* `[hidden]` tiene que ganar SIEMPRE. Sin esto, la regla de abajo —que da
   `display:flex` a la portada— pesaba más que el `.dm-app-auth-view[hidden]
   {display:none}` de la hoja base, así que pulsar «Iniciar sesión» marcaba la
   vista como oculta y la portada seguía pintada encima del formulario. Se vio
   midiendo: la pantalla se movía 68 px y no cambiaba de vista. */
html.dm-app-mode .dm-app-auth-view[hidden] { display: none !important; }

/* La portada no es una tarjeta: es la pantalla. La tarjeta es la de dentro.
   Sin esto había una tarjeta dentro de otra, que es la manera más rápida de
   que una pantalla parezca un formulario aunque no lo sea. */
html.dm-app-mode .dm-app-auth-view.dm-acc-portada {
  width: 100%;
  max-width: none;
  min-height: calc(100dvh - 48px - var(--dm-app-safe-top, 0px) - var(--dm-app-safe-bottom, 0px));
  margin: 0;
  padding: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  display: flex;
  flex-direction: column;
}

/* La mitad de arriba es de la marca: el arte de la portada web (§51) y, bajo
   él, el logotipo y el lema (§46). Aquí hubo un isotipo blanco de 60 px
   encima de la ilustración: un logotipo puesto sobre el dibujo que ya dice de
   quién es la app. Se quitó, no se movió. */

/* ── La tarjeta ────────────────────────────────────────────────────────────
   `margin-top:auto` la baja hasta donde llega el pulgar. En una pantalla de
   844 px, un botón centrado a media altura se toca con la otra mano. */
html.dm-app-mode .dm-acc-tarjeta {
  margin-top: auto;
  padding: var(--s-7) var(--s-6) var(--s-6);
  border-radius: var(--r-4);
  background: var(--c-elevado);
  box-shadow: 0 28px 70px rgba(10, 10, 20, .35), 0 5px 18px rgba(10, 10, 20, .18);
}

html.dm-app-mode .dm-acc-saludo {
  max-width: 16ch;
  margin: 0 0 var(--s-6);
  font: var(--w-regular) var(--t-large-title)/1.06 var(--f-display);
  letter-spacing: -.022em;
  color: var(--c-label);
  text-wrap: balance;
}
/* El nombre en su propia línea, como se saluda de verdad. */
html.dm-app-mode .dm-acc-nombre {
  display: block;
  color: var(--c-label-2);
}

/* Con `.dm-acc-tarjeta` delante a propósito: la regla de botones de §13 es
   `html.dm-app-mode body :is(.dm-app-primary, …)` y pesa más que un
   `html.dm-app-mode .dm-acc-entrar`. Medido: el botón salía de 50 px. */
html.dm-app-mode body .dm-acc-tarjeta .dm-acc-entrar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  width: 100%;
  /* 56 px: la acción que se toca todos los días, no la que cumple el mínimo. */
  min-height: 56px;
  border-radius: var(--r-3);
  font: var(--w-semibold) var(--t-headline)/1 var(--f-ui);
}
html.dm-app-mode body .dm-acc-tarjeta .dm-acc-entrar svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

html.dm-app-mode .dm-acc-otra {
  display: block;
  width: 100%;
  min-height: var(--toque);
  margin: var(--s-2) 0 0;
  font: var(--w-medium) var(--t-subhead)/1 var(--f-ui);
  color: var(--c-tinte);
  text-align: center;
  /* Sin subrayado: es un control suelto debajo de un botón, no un enlace
     dentro de una frase. El subrayado se queda donde hace falta —«Crear
     perfil», que va metido en una línea de texto. */
  text-decoration: none;
}

html.dm-app-mode .dm-acc-portada .dm-app-auth-trust {
  margin: var(--s-3) 0 0;
  font: var(--w-regular) var(--t-caption-2)/1.4 var(--f-ui);
  color: var(--c-label-3);
}

html.dm-app-mode .dm-acc-alterna {
  margin: var(--s-5) 0 0;
  font: var(--w-regular) var(--t-subhead)/1.45 var(--f-ui);
  color: var(--c-label-2);
  text-align: center;
  text-wrap: balance;
}

/* ── Lo que se puede hacer sin cuenta ──────────────────────────────────────
   El directorio es público y las vacantes también: enseñarlos antes de pedir
   nada es la única razón por la que alguien sin cuenta se queda. */
/* Es un `<nav>`, y las páginas que alojan el acceso traen reglas para `nav`:
   la portada web lo hace `position:fixed` con fondo blanco y z-index 300 —su
   barra de arriba—, y Términos y Privacidad, `sticky` de 52 px. Al cerrar
   sesión la app caía en la portada web y la fila flotaba encima del saludo y
   de «Iniciar sesión». Por eso aquí se declara todo lo que decide su sitio,
   no sólo lo que se quería cambiar. */
html.dm-app-mode .dm-acc-atajos {
  position: static;
  inset: auto;
  z-index: auto;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  align-items: stretch;
  justify-content: normal;
  gap: var(--s-2);
  width: auto;
  height: auto;
  margin: var(--s-6) 0 0;
  padding: var(--s-5) 0 0;
  border: 0;
  border-top: .5px solid var(--c-separador);
  background: none;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
html.dm-app-mode .dm-acc-atajo {
  display: grid;
  justify-items: center;
  align-content: start;
  gap: var(--s-2);
  min-height: var(--toque);
  padding: var(--s-2) var(--s-1);
  border-radius: var(--r-2);
  color: var(--c-label-2);
  font: var(--w-medium) var(--t-caption-1)/1.25 var(--f-ui);
  text-align: center;
  text-decoration: none;
}
html.dm-app-mode .dm-acc-atajo-icono {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: var(--r-max);
  background: var(--c-relleno);
  color: var(--c-tinte);
}
html.dm-app-mode .dm-acc-atajo svg {
  width: 21px;
  height: 21px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
html.dm-app-mode .dm-acc-atajo:active .dm-acc-atajo-icono { background: var(--c-relleno-2); }

/* ── Volver a la portada ───────────────────────────────────────────────────
   El formulario se abre desde la portada, así que tiene que poder cerrarse.
   Sin esto, la única salida de «iniciar sesión» era crear una cuenta. */
html.dm-app-mode .dm-acc-volver {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  min-height: var(--toque);
  margin: calc(var(--s-4) * -1) 0 var(--s-1);
  padding: 0;
  color: var(--c-label-2);
  text-decoration: none;
}
html.dm-app-mode .dm-acc-volver::before {
  content: "\2039";
  font-size: 1.5em;
  line-height: .8;
}

/* La franja de marca sólo tiene sentido con la portada delante. En las demás
   vistas el sitio es del formulario, que en un teléfono compite con el
   teclado: sin esto, «crear perfil» arrancaba con 112 px de nada arriba. */
html.dm-app-mode:not(.dm-acc-en-portada) .dm-app-auth-view { margin-top: calc(var(--s-8) + var(--s-6)); }

/* ── Crear perfil, en dos preguntas ────────────────────────────────────────
   Ocho campos de golpe medían 872 px en una pantalla de 844: al llegar, el
   botón de crear la cuenta estaba fuera de la vista y no había manera de
   saber cuánto faltaba. Los mismos ocho, partidos en «quién eres» y «dónde
   estás», caben y se cuentan.

   El `<form>` sigue siendo uno solo y los campos del paso escondido siguen en
   el documento: el envío, el autocompletado y el gestor de contraseñas del
   navegador funcionan igual que antes. */
html.dm-app-mode .dm-acc-paso {
  display: grid;
  gap: var(--s-4);
  margin: 0;
  padding: 0;
  border: 0;
}
html.dm-app-mode .dm-acc-paso[hidden] { display: none; }
html.dm-app-mode .dm-acc-paso > legend {
  margin: 0 0 var(--s-2);
  padding: 0;
  font: var(--w-semibold) var(--t-footnote)/1.2 var(--f-ui);
  color: var(--c-label-2);
}

/* Cuánto falta. Una barra que mide algo de verdad —pasos, no tiempo. */
html.dm-app-mode .dm-acc-pasos {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: 0 0 var(--s-5);
  font: var(--w-medium) var(--t-caption-1)/1 var(--f-ui);
  color: var(--c-label-2);
  white-space: nowrap;
}
html.dm-app-mode .dm-acc-pasos-barra {
  flex: 1 1 auto;
  height: 4px;
  border-radius: var(--r-max);
  background: var(--c-relleno-2);
  overflow: hidden;
}
html.dm-app-mode .dm-acc-pasos-barra > i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--c-tinte);
  transition: width var(--t-medio) var(--sal);
}

@media (prefers-reduced-motion: reduce) {
  html.dm-app-mode .dm-acc-pasos-barra > i { transition-duration: 120ms; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   38 · Explorar: una pantalla, no tres
   ───────────────────────────────────────────────────────────────────────────
   Categoría, resultados, filtrado, vacío, cargando y error no son pantallas
   distintas: son estados de la misma. Y el estado de categoría gastaba media
   pantalla antes de enseñar nada. Medido a 390×844:

     título editorial   35 px, dos renglones, «Personas y empresas creativas»
     bajada             tres renglones de texto
     antes de la lista  ~230 px de los 844
     nueve categorías   siete decían «0 perfiles»
     dos contadores     «9 categorías» arriba y «N perfiles» en cada renglón
     la última fila     tapada por la barra de pestañas

   En un teléfono, un titular editorial de 35 px es la portada de una revista
   delante de una herramienta. La cabecera de la app ya dice dónde se está y
   ya lleva el buscador: aquí basta una línea que diga qué se está mirando.
   ═══════════════════════════════════════════════════════════════════════════ */

/* El héroe deja de ser un héroe. */
html.dm-app-mode body .cat-hero {
  display: block !important;
  padding: var(--s-5) var(--margen) var(--s-3) !important;
  border-bottom: 0 !important;
  gap: 0 !important;
}
/* `clamp(29px, 9vw, 38px)` son 35 px en un teléfono de 390: un titular de
   portada de revista delante de una herramienta. Y va con `!important`, así
   que aquí hace falta otro. */
html.dm-app-mode body .cat-hero .hero-title,
html.dm-app-mode body .vac-hero .hero-title {
  margin: 0 !important;
  font-family: var(--f-display) !important;
  font-size: var(--t-title-2) !important;
  font-weight: var(--w-medium) !important;
  line-height: 1.15 !important;
  letter-spacing: -.016em !important;
  text-wrap: balance;
}
/* La bajada explica el sitio a quien llega de Google. Quien abrió la app ya
   entró: aquí sobra, y son tres renglones antes de lo que vino a hacer. */
html.dm-app-mode body .cat-hero .hero-sub,
html.dm-app-mode body .vac-hero .hero-sub,
html.dm-app-mode body .cat-hero-media,
html.dm-app-mode body .cat-hero figure { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   El blog y las novedades hablan otro idioma de clases
   ───────────────────────────────────────────────────────────────────────────
   Todo lo de arriba trata `.hero-eyebrow`, `.hero-title` y `.hero-sub`. El
   blog y las novedades usan `.eyebrow`, un `h1` pelado y `.listing-copy`, así
   que NADA de este tratamiento les llegaba: seguían siendo dos páginas web
   dentro de la app. Medido a 390×844 en /novedades: cejilla + título + bajada
   ocupaban 246 px —el 29 % de la pantalla— antes de la primera novedad.

   No es un patrón nuevo: son las mismas reglas, con los mismos motivos, dichas
   también en su vocabulario. La cejilla y la bajada se van por lo mismo que
   se van en las otras —la bajada explica el sitio a quien llega de Google, y
   quien abrió la app ya entró— y el título pasa a ser el título grande del
   sistema.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body :is(.listing-hero .eyebrow, .listing-hero .listing-copy,
  .blog-hero .eyebrow, .blog-hero .listing-copy) { display: none !important; }

html.dm-app-mode body :is(.listing-hero, .blog-hero) .listing-inner > h1,
html.dm-app-mode body :is(.listing-hero, .blog-hero) > h1 {
  margin: 0 0 var(--s-2) !important;
  font: var(--w-bold) var(--t-large-title)/var(--t-large-title-lh) var(--f-display) !important;
  letter-spacing: -.022em !important;
  color: var(--c-label) !important;
  text-wrap: balance;
}
html.dm-app-mode body :is(.listing-hero, .blog-hero) h1 em {
  font-style: italic;
  font-weight: var(--w-regular);
}

/* Las pestañas de categoría son el filtro de la pantalla y median 35×36: por
   debajo de los 44 pt del sistema en las dos dimensiones. Se les da la forma
   que ya tienen los filtros de Buscar —píldora, no subrayado— porque es el
   mismo gesto en la misma app, y el área de toque llega a 44 sin engordar el
   dibujo, igual que en los chips. */
html.dm-app-mode body .category-tabs {
  gap: var(--s-2) !important;
  margin: 0 0 var(--s-4);
  padding-bottom: var(--s-1);
  flex-wrap: nowrap;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
html.dm-app-mode body .category-tabs::-webkit-scrollbar { display: none; }
html.dm-app-mode body .category-tab {
  position: relative;
  flex: 0 0 auto;
  min-height: 34px;
  padding: var(--s-1) var(--s-3) !important;
  border: 0 !important;
  border-radius: 999px;
  background: var(--c-relleno);
  font: var(--w-medium) var(--t-subhead)/1.2 var(--f-ui) !important;
  color: var(--c-label-2) !important;
}
html.dm-app-mode body .category-tab.active {
  background: var(--c-label);
  color: var(--c-bg) !important;
  font-weight: var(--w-semibold) !important;
}
html.dm-app-mode body .category-tab::after {
  content: '';
  position: absolute;
  inset: 50% 0 auto;
  height: var(--toque);
  transform: translateY(-50%);
}

/* ── Los controles sueltos que se quedaron cortos ────────────────────────────
   Medidos a 390×844 en las nueve pantallas que nunca habían pasado por la
   regla. Todos por debajo de los 44 pt del sistema, y todos SUELTOS —no van
   dentro de una frase, así que no les aplica la excepción de WCAG 2.5.8—:

     .section-link            165×33   «Ver todas las publicaciones →»
     #featured-prev/next       34×34   las flechas del destacado
     .pagination-arrow         36×36   y los números de página
     .meta-name               200×22   la firma del artículo
     .article-cta-primary     302×42
     .article-share button     87×42   los cinco de compartir
     .article-share a         139×23   «Ver más publicaciones →»
     .dm-edit-btn             324×34   «Editar perfil»
     a.logo                   132×32   la marca de recuperar contraseña

   Donde el dibujo puede crecer sin estorbar, crece (`min-height`). Donde no
   —una flecha de 34, un enlace subrayado dentro de una fila de texto— el área
   crece y el dibujo no, con el `::after` absoluto que ya usan los chips y que
   la función de medida reconoce. */
html.dm-app-mode body :is(.section-link, #featured-prev, #featured-next,
  .pagination-arrow, .pagination-pages button, .meta-name,
  .article-share a, main.card > .logo) {
  position: relative;
}
html.dm-app-mode body :is(.section-link, #featured-prev, #featured-next,
  .pagination-arrow, .pagination-pages button, .meta-name,
  .article-share a, main.card > .logo)::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(100%, var(--toque));
  height: max(100%, var(--toque));
  transform: translate(-50%, -50%);
}

html.dm-app-mode body :is(.article-cta-primary, .article-cta-secondary,
  .share-actions button, .dm-edit-btn) {
  min-height: var(--toque);
  /* Ancho también: el botón de X mide 36 porque su rótulo es una letra. Un
     control no deja de necesitar el dedo por tener el nombre corto. */
  min-width: var(--toque);
  align-items: center;
  justify-content: center;
}
/* El `display` va aparte, y `.dm-edit-btn` NO entra.
   
   Estaba dentro, y con eso este bloque le ganaba por especificidad a la regla
   que lo esconde —`.dm-edit-btn{display:none}`, que `perfil-integration.js`
   inyecta— y volvía a enseñarlo. Resultado medido: dentro de la app instalada,
   cualquiera que abriera el perfil de otra persona veía «Editar perfil» al
   lado de «Hacer match» y de «Reportar perfil», ofreciéndose a editar un
   perfil ajeno. Se hereda `inline-flex` sólo cuando toca: en el tuyo. */
html.dm-app-mode body :is(.article-cta-primary, .article-cta-secondary,
  .share-actions button) {
  display: inline-flex;
}
html.dm-app-mode body.dm-owner-profile .dm-edit-btn { display: inline-flex; }

/* ═══════════════════════════════════════════════════════════════════════════
   Las secciones del panel, que son once y sólo se había medido una
   ───────────────────────────────────────────────────────────────────────────
   El panel es UNA ruta con once pantallas que cambia `showPage()`, así que la
   auditoría lo abría en Inicio y ahí se quedaba. Editar perfil, membresía,
   referidos, configuración y ayuda no las había medido nadie. Medido a
   390×844: 47 textos por debajo del contraste y 10 controles por debajo de
   44 pt.

   El 100 % de los 47 era una sola variable —`--text-xsoft`, 2.30:1— y se
   arregla arriba, reapuntándola. Lo que queda aquí son colores y medidas
   escritos a mano.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Los controles que se quedaron cortos. Todos son botones sueltos con su
   rótulo dentro, así que crecen: no hay ninguno cuyo dibujo estorbe a 44. */
html.dm-app-mode body :is(.edit-back, .ptoggle-btn, .pp-cta, .plan-cta,
  .ref-copy-btn, .ref-copy-msg, .cfg-btn-edit, .help-search button) {
  min-height: var(--toque);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Membresía: tres colores de marca que no llegan al mínimo sobre su fondo.
   El oliva sobre hueso (2.58:1), el naranja de Fundador (1.86:1) y el gris
   del botón del plan en curso sobre su propia piedra (3.35:1). Se conservan
   como color —son señas de la marca— moviendo la luz hasta que se lean.
  
   Y CADA APARIENCIA POR SU LADO. La primera versión de esto puso los tres
   oscuros a secas: en claro salió bien y en oscuro los dejó a 3.27 y 2.67,
   porque un color legible sobre hueso no lo es sobre casi negro. Es el mismo
   error que este archivo lleva media vida arreglando en otros, cometido aquí
   por escribir el color en vez del escalón. */
@media (prefers-color-scheme: light) {
  html.dm-app-mode body .ptoggle-save { color: #6E6A21 !important; }
  html.dm-app-mode body .pp-fundador-label { color: #8A4A00 !important; }
}
@media (prefers-color-scheme: dark) {
  html.dm-app-mode body .ptoggle-save { color: var(--c-tinte) !important; }
  html.dm-app-mode body .pp-fundador-label { color: #F0B571 !important; }
}
/* El botón del plan en curso: se le da el par entero —fondo y tinta— para que
   no dependa de qué haya debajo. */
html.dm-app-mode body :is(.pp-cta.plan-cta, .plan-cta[disabled]) {
  background: var(--c-relleno-2) !important;
  color: var(--c-label) !important;
}

/* Referidos: la banda morada escribe su texto en el mismo lila a 40, 50 y
   62 % de opacidad. Sobre el ciruela del fondo eso es 2.37, 2.99 y 3.90:1.
   Es el mismo color; lo que falta es opacidad. */
html.dm-app-mode body :is(.ref-band-label, .ref-band-desc) {
  color: rgba(238, 214, 250, .96) !important;
}
html.dm-app-mode body .ref-band-dash { color: rgba(238, 214, 250, .8) !important; }

/* Y los dos que se quedaban a un pelo: 4.41 y 4.38 contra 4.5. `--text-soft`
   es 4.98:1 sobre el papel, pero estos dos van sobre una tarjeta que ya no es
   el papel. Al escalón de al lado. */
html.dm-app-mode body :is(.ref-ej-sub, .pab-estado, .pp-estado) {
  color: var(--c-label-2) !important;
}

/* El verde de «Activo» sobre su propio fondo verde al 12 %: 4.38:1, a un pelo
   de 4.5. Es el mismo verde con menos luz — sigue siendo verde, y ahora se
   lee. SÓLO en claro: el bloque de oscuro ya lo aclara a `#7FC9A0`, y ponerlo
   aquí sin condición pisaba aquél y dejaba «Activo» a 3.44:1 en oscuro.

   Y #1F7A4D tampoco bastaba: la prueba medía cada texto contra el primer
   fondo OPACO y se saltaba los translúcidos. Componiéndolos —la pastilla
   verde al 8 % sobre la tarjeta beige al 32 % sobre el papel—, «Activa» en
   Membresía era 4.24:1. Lo mismo destapó otras dos variables de la página
   que la app no había traído a su escala en claro: `--text-soft` (#6F6F6F;
   «Desactivada» en Mis vacantes, 4.38:1) y `--c-olive` (#9A9534: 3.1:1 ya
   sobre el papel; «1 mes» de Referidos, 2.87:1 a 28 px). Pasan a los
   escalones de la app: 5.19, 4.79 y 4.83:1 sobre esos mismos fondos. */
@media (prefers-color-scheme: light) {
  html.dm-app-mode {
    --green: #1A6B43;
    --text-soft: var(--c-label-3);
    --c-olive: var(--c-tinte);
  }
}

/* La pestaña activa del dock va en el acento sobre el velo oscuro: 4.14:1.
   El acento de la marca no se cambia —es el color con el que se reconoce— y
   se aclara SÓLO donde el fondo es oscuro, que es donde el número falla. */
html.dm-app-mode #dm-app-bottom-nav[data-glass-context="dark"]
  .dm-app-nav-item[aria-current="page"] {
  color: #E4DBA2;
}

/* Un contador arriba y otro en cada renglón cuentan cosas distintas y se leen
   como el mismo dato repetido. El de cada fila es el que sirve. */
html.dm-app-mode body .cat-header-count { display: none !important; }
html.dm-app-mode body .cat-header {
  margin: 0 0 var(--s-2) !important;
  padding: 0 var(--margen);
}
html.dm-app-mode body .cat-header-label {
  font: var(--w-semibold) var(--t-footnote)/1.2 var(--f-ui);
  letter-spacing: 0;
  text-transform: none;
  color: var(--c-label-2);
}

/* `01 02 03` no ordena nada: las categorías no son una secuencia. */
html.dm-app-mode body .cat-num-wrap { display: none !important; }

html.dm-app-mode body .cat-list-section {
  /* El sitio de la barra de pestañas, que tapaba la última categoría. */
  padding: 0 0 calc(var(--s-8) + var(--dm-app-safe-bottom, 0px) + 56px) !important;
  max-width: none !important;
}
html.dm-app-mode body .cat-list {
  margin: 0 var(--margen);
  border-top: 0;
  border-radius: var(--r-3);
  background: var(--c-elevado);
  box-shadow: inset 0 0 0 .5px var(--c-separador);
  overflow: hidden;
}
html.dm-app-mode body .cat-item {
  min-height: var(--toque);
  padding: var(--s-3) var(--s-4) !important;
  border-bottom: 0 !important;
}
html.dm-app-mode body .cat-item + .cat-item { box-shadow: inset 0 .5px 0 var(--c-separador); }
/* El desplazamiento al pasar el cursor es de ratón; en un dedo sólo hace que
   la fila salte al tocarla. */
html.dm-app-mode body .cat-item:hover { padding-left: var(--s-4) !important; }
html.dm-app-mode body .cat-item::after { content: none !important; }
html.dm-app-mode body .cat-item:active { background: var(--c-relleno); }

/* Las vacías siguen estando —son categorías reales— pero dejan de invitar. */
html.dm-app-mode body .cat-item[data-vacia="1"] { opacity: .55; }
html.dm-app-mode body .cat-item[data-vacia="1"] .cat-arrow { visibility: hidden; }

/* ═══════════════════════════════════════════════════════════════════════════
   39 · El dock
   ───────────────────────────────────────────────────────────────────────────
   Lo que había era correcto y no se sentía de una app: una barra flotante con
   una cápsula de relleno plano y los cinco glifos idénticos en trazo, activo
   incluido. En el sistema, un elemento de barra seleccionado no cambia sólo de
   color — cambia de PESO: el glifo pasa de trazo a sólido. Es la señal que más
   dice «esto es nativo», y no cuesta nada: los dos dibujos viven en el mismo
   `<svg>` y se alternan con `opacity`, sin segunda petición y sin que el
   JavaScript tenga que saber qué pestaña está activa.

   ── El coste, que es la otra mitad del encargo ─────────────────────────────
   · UNA superficie con `backdrop-filter` en todo el dock. Ni una por pestaña,
     ni una en la cápsula: el cristal es del contenedor y sólo de él.
   · La cápsula se mueve con `transform` y nada más. Sin `filter`, sin
     `border-radius` animado, sin `box-shadow` animado — las tres cosas
     repintan, y estaban las tres.
   · `contain: layout paint` ya estaba y se conserva: aísla al dock del resto
     del documento, así que moverse dentro de él no invalida nada de fuera.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode #dm-app-bottom-nav {
  /* 4 px de aire alrededor de las pestañas. Menos aprieta la cápsula contra
     el borde del cristal; más obliga a subir el alto total. */
  padding: 4px;
  min-height: 58px;
  border-radius: 26px;
  /* La sombra del dock es más honda que la del resto del material: es lo que
     lo separa del contenido que pasa por debajo. Una sola sombra, estática. */
  box-shadow:
    0 10px 34px rgba(30, 31, 48, .13),
    0 2px 8px rgba(30, 31, 48, .07),
    inset 0 0 0 .5px var(--m-borde),
    /* La luz del canto. Es lo que separa un cristal de una franja pintada. */
    inset 0 .5px 0 0 var(--m-brillo);
}

/* La cápsula. Un material propio —no un color— para que se lea como una
   superficie encima del cristal y no como un subrayado. */
html.dm-app-mode .dm-app-nav-indicator {
  top: 4px;
  bottom: 4px;
  left: 4px;
  width: calc((100% - 8px) / var(--dm-app-nav-total, 5));
  border-radius: 22px;
  background: var(--c-relleno-2);
  box-shadow: inset 0 0 0 .5px var(--m-borde);
  /* Sólo transform. La versión anterior animaba además `border-radius` y
     `filter`, que obligan a repintar la cápsula en cada cuadro del recorrido. */
  transition: transform var(--t-lento) var(--sal);
  will-change: transform;
}
html.dm-app-mode .dm-app-nav-indicator.is-moving {
  border-radius: 22px;
  filter: none;
  --dm-app-nav-scale: 1;
}

html.dm-app-mode .dm-app-nav-item {
  gap: 3px;
  padding: 5px 2px 4px;
  min-height: 50px;
  border-radius: 22px;
  color: var(--c-label-2);
}

/* El glifo vive en su propia caja para poder empujarse un pelo al pulsar sin
   arrastrar la etiqueta. Es el detalle que hace que la pestaña «responda». */
html.dm-app-mode .dm-app-nav-glifo {
  display: block;
  line-height: 0;
  transition: transform var(--t-tap) var(--sal);
}
html.dm-app-mode .dm-app-nav-item:active .dm-app-nav-glifo { transform: scale(.88); }

html.dm-app-mode .dm-app-nav-item svg { width: 25px; height: 25px; stroke-width: 1.7; }

/* Trazo por defecto, sólido cuando la pestaña está activa. El cruce es
   `opacity` en dos grupos que ocupan el mismo sitio: no hay salto de forma. */
html.dm-app-mode .dm-ico-dual { position: relative; }
html.dm-app-mode .dm-ico-dual .dm-ico-solido,
html.dm-app-mode .dm-ico-dual .dm-ico-linea {
  transition: opacity var(--t-tap) var(--sal);
}
html.dm-app-mode .dm-ico-dual .dm-ico-solido {
  opacity: 0;
  fill: currentColor;
  stroke: none;
}
html.dm-app-mode .dm-app-nav-item[aria-current="page"] .dm-ico-dual .dm-ico-linea { opacity: 0; }
html.dm-app-mode .dm-app-nav-item[aria-current="page"] .dm-ico-dual .dm-ico-solido { opacity: 1; }

html.dm-app-mode .dm-app-nav-rotulo {
  /* El rótulo del dock. El suelo era 10px — justo el tamaño del que se venía
     huyendo: la barra de pestañas estaba a 9 px y por eso existe el mínimo de
     11. El techo de 12 sí se queda: aquí caben cinco rótulos en una línea y
     dejarlos crecer los parte. */
  font: var(--w-semibold) clamp(11px, var(--t-caption-2), 12px)/1.1 var(--f-ui);
  letter-spacing: -.005em;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

html.dm-app-mode .dm-app-nav-item[aria-current="page"] {
  color: var(--c-tinte);
  filter: none;
}
/* El sólido ya lleva el peso: subir además el grosor del trazo lo emborrona. */
html.dm-app-mode .dm-app-nav-item[aria-current="page"] svg { stroke-width: 1.7; }

/* La barra entera no se escala al pulsar: sólo el glifo. Escalar el enlace
   arrastra la cápsula y el cristal con él, que es un repintado del dock
   completo por cada toque. */
html.dm-app-mode body .dm-app-nav-item:active { transform: none; }

@media (prefers-reduced-motion: reduce) {
  html.dm-app-mode .dm-app-nav-indicator,
  html.dm-app-mode .dm-app-nav-glifo,
  html.dm-app-mode .dm-ico-dual .dm-ico-solido,
  html.dm-app-mode .dm-ico-dual .dm-ico-linea { transition: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   40 · La cabecera, más baja
   ───────────────────────────────────────────────────────────────────────────
   Medía 65 px sin contar el área segura: 6 de aire, una pastilla de 52 y otros
   6. En una pantalla de 844 eso es el 7,7 % del alto gastado en una barra que
   contiene tres controles.

   Sin cápsula la cuenta es otra: 6 + 50 + 6. Los 50 son el control flotante
   —por encima del blanco de toque de 44, que es el mínimo, no el objetivo— y
   los 6 de arriba y abajo son el aire que separa las tres piezas del contenido
   sin dibujar una barra.

   TODA la geometría de la cabecera se decide aquí, y sólo aquí. Estuvo
   repartida entre esta sección y §31, las dos diciendo cuánto medía el control,
   y ganaba la de más abajo: los botones salían a 44 px cuando arriba ponía 50.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode #dm-app-header {
  padding: calc(6px + var(--dm-app-safe-top, 0px)) max(var(--margen), var(--dm-app-safe-right, 0px)) 6px max(var(--margen), var(--dm-app-safe-left, 0px));
  min-height: calc(50px + var(--dm-app-safe-top, 0px));
}

/* La fila mide lo que mide el control: sin cápsula no hay relleno propio ni
   columnas que cuadrar, y la marca ni siquiera está en el flujo. */
html.dm-app-mode .dm-app-bar { min-height: 50px; }

html.dm-app-mode .dm-app-bar-btn { width: var(--toque); height: var(--toque); }
html.dm-app-mode .dm-app-bar-btn svg { width: 21px; height: 21px; }
html.dm-app-mode .dm-app-bar-btn.dm-app-control { width: 50px; height: 50px; }
html.dm-app-mode .dm-app-control svg { width: 22px; height: 22px; }

html.dm-app-mode .dm-app-bar-marca {
  height: 19px;
  /* Lo que ocupan los dos controles, sus márgenes y un respiro a cada lado.
     En 390 deja 132; en 375, 121. La marca nunca llega a tocarlos. */
  max-width: min(132px, calc(100vw - 176px));
}

/* Por debajo de 361 —el iPhone SE y lo que quede más estrecho— el control baja
   a 46. Sigue por encima del blanco de toque y devuelve ocho píxeles a la
   marca, que es la pieza que se queda sin sitio primero. */
@media (max-width: 360px) {
  html.dm-app-mode #dm-app-header { min-height: calc(46px + var(--dm-app-safe-top, 0px)); }
  html.dm-app-mode .dm-app-bar { min-height: 46px; }
  html.dm-app-mode .dm-app-bar-btn.dm-app-control { width: 46px; height: 46px; }
  html.dm-app-mode .dm-app-bar-marca { height: 17px; max-width: min(120px, calc(100vw - 160px)); }
}

/* La ficha de detalle usa la misma altura: la barra no puede cambiar de tamaño
   al entrar en un perfil, o la pantalla da un salto de 13 px al navegar. */
html.dm-app-mode .dm-app-bar.is-detalle { min-height: 44px; }

/* ═══════════════════════════════════════════════════════════════════════════
   41 · La hoja del menú
   ───────────────────────────────────────────────────────────────────────────
   Un panel que sube desde abajo, no un cuadro centrado. La diferencia importa
   en el pulgar: lo que llega desde abajo se alcanza; lo que aparece en medio
   obliga a recolocar la mano.

   ── Dónde va el cristal, y dónde no ────────────────────────────────────────
   La hoja es OPACA. Es contenido —una lista de destinos que hay que leer—, y
   la guía del material lo dice sin rodeos: «Don't use Liquid Glass in the
   content layer». El cristal se queda donde está el armazón: el dock y la
   cabecera. Lo que sí lleva material es el VELO de detrás, que es lo que
   convierte la pantalla anterior en fondo.

   Y es además la decisión barata: desenfocar 390×660 px de hoja cuesta cada
   cuadro de la subida; desenfocar el velo cuesta una vez, porque debajo no se
   mueve nada mientras la hoja está abierta.
   ═══════════════════════════════════════════════════════════════════════════ */

/* El agarre en su propia caja: así puede tener 22 px de alto tocables sin que
   la barrita mida 22 px. */
html.dm-app-mode .dm-app-sheet-agarre {
  display: grid;
  place-items: center;
  height: 20px;
  margin: -2px 0 6px;
}
html.dm-app-mode .dm-app-sheet-handle {
  display: block;
  width: 36px;
  height: 5px;
  margin: 0;
  border-radius: var(--r-max);
  background: var(--c-relleno-3);
}

html.dm-app-mode .dm-app-sheet-backdrop {
  background: rgba(0, 0, 0, .34);
  /* 3 px basta para que el fondo deje de competir. Por encima de ~20 px
     Safari lo paga caro y aquí no aporta nada. */
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

html.dm-app-mode .dm-app-menu-hoja {
  padding-top: 6px;
  max-height: min(86dvh, 820px);
}

/* ── Quién eres ──────────────────────────────────────────────────────────── */
html.dm-app-mode .dm-app-menu-cabecera {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  align-items: center;
  gap: var(--s-3);
  margin: 0 var(--toque) var(--s-4) 0;
}
html.dm-app-mode .dm-app-menu-retrato {
  width: 46px;
  height: 46px;
  border-radius: var(--r-max);
  overflow: hidden;
  display: grid;
  place-items: center;
  background: #6B7110;
  color: #FFFEFA;
  font: var(--w-semibold) var(--t-callout)/1 var(--f-ui);
}
html.dm-app-mode .dm-app-menu-retrato img { width: 100%; height: 100%; object-fit: cover; }
html.dm-app-mode .dm-app-menu-quien { min-width: 0; display: grid; gap: 1px; }
html.dm-app-mode .dm-app-menu-nombre {
  margin: 0;
  font: var(--w-semibold) var(--t-headline)/var(--t-headline-lh) var(--f-ui);
  letter-spacing: -.01em;
  color: var(--c-label);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
html.dm-app-mode .dm-app-menu-cuenta {
  font: var(--w-regular) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui);
  color: var(--c-label-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
html.dm-app-mode .dm-app-menu-punto { margin: 0 .35em; }

/* ── Los grupos ──────────────────────────────────────────────────────────── */
html.dm-app-mode .dm-app-menu-grupo:not(:last-of-type) { margin-bottom: var(--s-4); }
html.dm-app-mode .dm-app-menu-grupo:empty { display: none; }
html.dm-app-mode .dm-app-menu-titulo {
  margin: 0 0 var(--s-2) var(--s-3);
  font: var(--w-medium) var(--t-footnote)/1.2 var(--f-ui);
  letter-spacing: 0;
  color: var(--c-label-3);
}
html.dm-app-mode .dm-app-menu-lista {
  border-radius: var(--r-3);
  background: var(--c-bg-agrupado);
  overflow: hidden;
}

html.dm-app-mode .dm-app-menu-fila {
  position: relative;
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr) 18px;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  min-height: var(--toque);
  padding: var(--s-2) var(--s-3);
  border: 0;
  background: transparent;
  color: var(--c-label);
  text-align: left;
  cursor: pointer;
}
/* Separador metido hasta donde empieza el texto: de borde a borde una lista
   parece una tabla. */
html.dm-app-mode .dm-app-menu-fila + .dm-app-menu-fila::before {
  content: '';
  position: absolute;
  top: 0;
  left: calc(var(--s-3) + 26px + var(--s-3));
  right: 0;
  height: .5px;
  background: var(--m-separador);
}
html.dm-app-mode .dm-app-menu-fila:active { background: var(--c-relleno); }
html.dm-app-mode body .dm-app-menu-fila:active { transform: none; }

html.dm-app-mode .dm-app-menu-icono { display: grid; place-items: center; color: var(--c-tinte); }
html.dm-app-mode .dm-app-menu-icono svg {
  width: 21px; height: 21px;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
html.dm-app-mode .dm-app-menu-texto { min-width: 0; display: grid; gap: 1px; }
html.dm-app-mode .dm-app-menu-rotulo {
  font: var(--w-regular) var(--t-body)/var(--t-body-lh) var(--f-ui);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
html.dm-app-mode .dm-app-menu-nota {
  font: var(--w-regular) var(--t-footnote)/1.2 var(--f-ui);
  color: var(--c-label-3);
}
html.dm-app-mode .dm-app-menu-flecha { display: grid; place-items: center; color: var(--c-label-3); }
html.dm-app-mode .dm-app-menu-flecha svg {
  width: 15px; height: 15px;
  fill: none; stroke: currentColor; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
}

html.dm-app-mode .dm-app-menu-salida { margin-top: var(--s-5); }
html.dm-app-mode .dm-app-menu-salida .dm-app-sheet-action {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  width: 100%;
  text-align: center;
}
html.dm-app-mode .dm-app-menu-salida .dm-app-sheet-action svg { margin: 0; }

/* ═══════════════════════════════════════════════════════════════════════════
   42 · Guardados
   ───────────────────────────────────────────────────────────────────────────
   Un selector segmentado, no dos destinos de menú. La pastilla se mueve con
   `transform` y nada más —el mismo criterio que la del dock—, y los rótulos
   cambian de color con una transición corta; ninguna de las dos cosas repinta
   nada fuera del control.

   La pastilla va DEBAJO de los rótulos (`z-index`), no encima con mezcla: dos
   capas translúcidas superpuestas emborronan el texto justo del segmento que
   hay que poder leer.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode .dm-guardados-selector {
  /* Pegado bajo la cabecera.
  
     Estaba en flujo normal, arriba del todo, y eso dejaba la memoria de scroll
     sin nada que recordar: para tocar el selector había que subir hasta
     arriba, así que la posición que se guardaba era siempre 0. Un control que
     sólo se puede usar desde el principio de la lista no puede prometer que te
     devuelve a donde estabas.
  
     Pegado, cambiar de lista se hace desde donde uno esté, y la altura
     guardada significa algo. Es además el patrón del sistema: el segmentado se
     queda y la lista corre por debajo. */
  position: sticky;
  top: calc(54px + var(--dm-app-safe-top, 0px));
  z-index: 2;
  display: grid;
  grid-template-columns: repeat(var(--dm-guardados-total, 2), minmax(0, 1fr));
  gap: 0;
  margin: 0 0 var(--s-4);
  padding: 3px;
  border-radius: var(--r-3);
  background: var(--c-relleno);
  isolation: isolate;
}

/* El fondo de la página, detrás del selector y hasta los márgenes.
   `--c-relleno` es translúcido al 6 %: sin esto, las tarjetas se leerían por
   debajo del segmentado al pasar. Va con `z-index: -1` dentro del propio
   contexto de apilamiento que crea `isolation`, así que tapa lo que scrollea
   por detrás sin taparse a sí mismo la pastilla ni los rótulos. */
html.dm-app-mode .dm-guardados-selector::before {
  content: '';
  position: absolute;
  z-index: -1;
  inset: -10px calc(var(--margen) * -1);
  background: var(--c-bg);
}

html.dm-app-mode .dm-guardados-pastilla {
  position: absolute;
  z-index: 0;
  top: 3px;
  bottom: 3px;
  left: 3px;
  width: calc((100% - 6px) / var(--dm-guardados-total, 2));
  border-radius: calc(var(--r-3) - 3px);
  background: var(--c-elevado);
  box-shadow: 0 1px 3px rgba(30, 31, 48, .10), inset 0 0 0 .5px var(--m-borde);
  transition: transform var(--t-lento) var(--sal);
  will-change: transform;
}

html.dm-app-mode .dm-guardados-seg {
  position: relative;
  z-index: 1;
  min-height: 38px;
  padding: var(--s-1) var(--s-2);
  border: 0;
  border-radius: calc(var(--r-3) - 3px);
  background: transparent;
  color: var(--c-label-2);
  font: var(--w-medium) var(--t-subhead)/1.2 var(--f-ui);
  cursor: pointer;
  transition: color var(--t-tap) var(--sal);
}
html.dm-app-mode .dm-guardados-seg > span {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
html.dm-app-mode .dm-guardados-seg[aria-selected="true"] {
  color: var(--c-label);
  font-weight: var(--w-semibold);
}
/* El segmento no se escala al pulsar: arrastraría la pastilla con él. */
html.dm-app-mode body .dm-guardados-seg:active { transform: none; }
html.dm-app-mode body .dm-guardados-seg:active > span { opacity: .55; }

/* El área sensible llega a 44 pt sin que el control mida 44: es la regla de
   §5, la misma que usan los chips de filtro. */
html.dm-app-mode .dm-guardados-seg::after {
  content: '';
  position: absolute;
  inset: 50% 0 auto;
  height: var(--toque);
  transform: translateY(-50%);
}

@media (prefers-reduced-motion: reduce) {
  html.dm-app-mode .dm-guardados-pastilla { transition: none !important; }
}

/* ── El vacío ─────────────────────────────────────────────────────────────
   Sin ilustración: una imagen genérica de carpeta vacía ocupa 200 px para
   decir lo que la primera línea ya dice. Lo que sí hace falta es que la
   acción esté a un dedo del texto que la justifica. */
html.dm-app-mode .empty-state {
  padding: var(--s-8) var(--margen) var(--s-6);
  text-align: center;
}
html.dm-app-mode .empty-state .empty-msg {
  margin: 0 0 var(--s-2);
  font: var(--w-regular) var(--t-title-3)/var(--t-title-3-lh) var(--f-display);
  font-style: normal;
  letter-spacing: -.01em;
  color: var(--c-label);
}
html.dm-app-mode .empty-state .empty-sub {
  margin: 0 auto var(--s-5);
  max-width: 30ch;
  font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui);
  color: var(--c-label-2);
}
html.dm-app-mode .empty-state .empty-btn {
  min-height: var(--toque);
  padding: 0 var(--s-6);
  border: 0;
  border-radius: var(--r-max);
  background: var(--c-primario);
  color: var(--c-bg);
  font: var(--w-semibold) var(--t-callout)/1 var(--f-ui);
  letter-spacing: 0;
  text-transform: none;
}
html.dm-app-mode .empty-state .empty-btn:active { background: var(--c-primario); opacity: .9; }

/* El título grande de Guardados lleva el contador a su lado: es un dato de
   una línea y no merece un renglón propio. */
html.dm-app-mode .dm-guardados-cabecera {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  padding: var(--s-2) 0 var(--s-3);
}
html.dm-app-mode .dm-guardados-cabecera h1 { flex: 0 1 auto; }
html.dm-app-mode .dm-guardados-cabecera .page-count {
  flex: 0 0 auto;
  font: var(--w-regular) var(--t-subhead)/1.2 var(--f-ui);
  color: var(--c-label-3);
  letter-spacing: 0;
  text-transform: none;
}

/* La barra de título del panel, escondida en las pantallas que ya llevan
   título grande. `hidden` solo no basta: `.page-topbar` declara `display:flex`
   y una declaración de display gana siempre al `[hidden]` del navegador. */
html.dm-app-mode .page-topbar[hidden] { display: none !important; }

/* `.page-inner` reservaba 44 px arriba para la barra fija del escritorio. En
   la app esa barra la reserva el `padding-top` del cuerpo, así que aquí eran
   44 px de nada al principio de CADA pantalla del panel: Guardados, Mi perfil,
   Membresía, Referidos, Configuración y Ayuda. */
html.dm-app-mode .page-inner {
  padding-top: var(--s-1);
  padding-left: max(var(--margen), var(--dm-app-safe-left, 0px));
  padding-right: max(var(--margen), var(--dm-app-safe-right, 0px));
}

/* ═══════════════════════════════════════════════════════════════════════════
   43 · Inicio, por capas
   ───────────────────────────────────────────────────────────────────────────
   Lo que había era información útil apilada en un solo plano: un saludo
   editorial de tres renglones, una tarjeta de 430 px con el nombre, el estado,
   las cifras, la barra de completitud, una nota de dos frases y tres botones,
   y debajo tres bloques más. Todo del mismo tamaño y del mismo peso.

   Ahora hay tres capas, y cada una responde a una pregunta distinta:

     1. Quién eres          — el saludo, corto.
     2. Cómo va             — las cifras, en su propia franja.
     3. Qué puedes hacer    — la tarjeta del perfil, con sus acciones.

   Las cifras salen de la tarjeta porque son lo que alguien viene a mirar
   cuando ya publicó. Dentro competían con todo lo demás.
   ═══════════════════════════════════════════════════════════════════════════ */

/* El saludo. `--t-title-1` en vez de `--t-large-title`: con el nombre dentro,
   el título grande se partía en tres renglones y gastaba 207 px de los 844. */
html.dm-app-mode body :is(#page-inicio .hero-title-pro, #page-inicio .hero-banner h1, #page-inicio .hero-banner h2) {
  font: var(--w-bold) var(--t-title-1)/var(--t-title-1-lh) var(--f-display) !important;
  text-wrap: balance;
}
html.dm-app-mode #page-inicio .hero-banner { padding-bottom: var(--s-2); }

/* El dibujo de las píldoras: es la marca, no un dato. En un teléfono compite
   con el saludo por el ancho y gana el que tiene color. Se reduce y se aparta;
   no se borra, que es identidad. */
html.dm-app-mode #page-inicio .hero-pro-row {
  align-items: center;
  gap: var(--s-3);
}
html.dm-app-mode #page-inicio .hero-pro-graphic {
  flex: 0 0 auto;
  width: 66px;
  opacity: .9;
}
html.dm-app-mode #page-inicio .hero-pro-graphic svg { width: 66px; height: 66px; }

/* La franja de cifras ───────────────────────────────────────────────────── */
html.dm-app-mode .dm-app-cifras { margin: 0 var(--margen) var(--s-5); }
html.dm-app-mode .dm-app-cifras .perfil-metricas { max-width: none; margin: 0; }

html.dm-app-mode .pm-titulo {
  margin: 0 0 var(--s-2) !important;
  font: var(--w-semibold) var(--t-footnote)/1.2 var(--f-ui) !important;
  letter-spacing: 0 !important;
  text-transform: none !important;
  color: var(--c-label-3) !important;
}

/* Baldosas, no cifras sueltas. Cada una es una superficie con su propio
   fondo: es lo que las convierte en tres datos comparables en vez de tres
   números flotando a distintas alturas. */
html.dm-app-mode .pm-stats {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-2);
}
html.dm-app-mode .pm-stat {
  position: relative;
  gap: 0;
  padding: var(--s-3) var(--s-2) var(--s-2);
  border-radius: var(--r-2);
  background: var(--c-bg-agrupado);
  text-align: left;
}
html.dm-app-mode .pm-stat b {
  font: var(--w-semibold) var(--t-title-2)/1 var(--f-display) !important;
  letter-spacing: -.02em !important;
  color: var(--c-label) !important;
}
html.dm-app-mode .pm-stat > span {
  margin-top: 2px !important;
  font: var(--w-medium) var(--t-caption-1)/1.2 var(--f-ui) !important;
  letter-spacing: 0 !important;
  text-transform: none !important;
  color: var(--c-label-2) !important;
  /* Con la letra grande «Recomendaciones» parte la baldosa; que se corte con
     puntos es mejor que descuadrar las tres. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
html.dm-app-mode .pm-stat small {
  font: var(--w-regular) var(--t-caption-2)/1.2 var(--f-ui) !important;
  color: var(--c-label-3) !important;
}
/* Un cero no se apaga: es un dato, y atenuarlo lo convierte en un error. */
html.dm-app-mode .pm-stat.is-cero { opacity: 1; }
html.dm-app-mode .pm-stat.is-cero b { color: var(--c-label-3) !important; }

/* La tendencia, arriba a la derecha de su baldosa: es un adjetivo de esa
   cifra, no una cuarta cifra. */
html.dm-app-mode .pm-delta {
  position: absolute;
  top: var(--s-2);
  right: var(--s-2);
  padding: 1px 5px !important;
  border-radius: var(--r-max) !important;
  font: var(--w-semibold) var(--t-caption-2)/1.4 var(--f-ui) !important;
  font-style: normal !important;
}

html.dm-app-mode .pm-nota {
  margin: var(--s-3) 0 0 !important;
  font: var(--w-regular) var(--t-caption-1)/1.45 var(--f-ui) !important;
  color: var(--c-label-3) !important;
}
html.dm-app-mode .pm-comparativa { margin-top: var(--s-3) !important; }

/* La tarjeta del perfil se queda con lo suyo y deja de ser un contenedor de
   todo. Sin las cifras dentro baja de 430 px a poco más de la mitad. */
html.dm-app-mode .psb {
  padding: var(--s-4);
  border-radius: var(--r-3);
  background: var(--c-bg-agrupado);
}
html.dm-app-mode .psb-actions {
  display: grid;
  gap: var(--s-2);
  margin-top: var(--s-4);
}
/* Los dos secundarios comparten renglón: son alternativas entre sí, y en
   columna leen como tres pasos de una secuencia que no existe. */
html.dm-app-mode .psb-actions .btn-ghost {
  grid-row: 2;
  min-height: var(--toque);
}
html.dm-app-mode .psb-actions .btn-ghost:nth-of-type(1) { grid-column: 1; }
html.dm-app-mode .psb-actions .btn-ghost:nth-of-type(2) { grid-column: 2; }
html.dm-app-mode .psb-actions { grid-template-columns: 1fr 1fr; }
html.dm-app-mode .psb-actions .btn-navy { grid-column: 1 / -1; min-height: var(--toque); }



/* ═══════════════════════════════════════════════════════════════════════════
   44 · Vacantes, como una pantalla de app
   ───────────────────────────────────────────────────────────────────────────
   La pantalla llegaba con la gramática de la página pública puesta. Medido a
   390 px, dónde empezaba la primera vacante:

     y=  54  héroe editorial, serif Petrona, «Vacantes en la industria creativa»
     y= 128  bloque de búsqueda: 187 px de alto, tres controles de ancho
             completo apilados (texto 48 · ubicación 48 · botón 50)
     y= 315  fila de filtros: 103 px, con «Más recientes» en un renglón propio
             pegado al borde derecho y los chips en otro
     y= 440  «8 vacantes»
     y= 462  la primera vacante

   440 de los 844 puntos de la pantalla gastados antes del contenido, y la
   mitad de ellos en decir dos veces dónde estás: el dock ya dice «Vacantes».

   Y la tira de chips, medida: `scrollWidth` 841 contra `clientWidth` 350, con
   20 px de margen a cada lado. Se desliza —eso funcionaba— pero un carril
   metido hacia dentro por los dos costados no se lee como algo que sigue: se
   lee como una caja que corta. Cinco de los siete filtros quedaban fuera sin
   nada que dijera que están ahí.

   ── Lo que cambia ──────────────────────────────────────────────────────────
   El título grande lo monta `montarVacantes()` con el mecanismo que ya existe
   (`.dm-app-titulo-grande`, el mismo de Guardados). Aquí va lo demás:

     · El héroe de la web se oculta. No se borra: esa copy es de la página
       pública, donde sigue siendo lo correcto.
     · La búsqueda pasa de tres pisos a un campo. Los dos campos siguen
       ahí —buscar por texto y por ciudad son dos cosas distintas y las dos
       funcionan— pero comparten un solo carril de 44 pt, que es la forma de
       un campo de búsqueda con ámbito. El botón «BUSCAR» se oculta porque
       Enter en cualquiera de los dos campos ya buscaba (está atado en
       `vacantes.html`) y ahora el teclado lo anuncia con `enterkeyhint`.
     · El carril de filtros sangra hasta los dos bordes, con el desplazamiento
       ajustado al margen: el primer chip se alinea con el título y el último
       llega entero. Lo que asoma por el borde derecho dice que hay más.
     · El contador pasa a encabezado de sección. No se mueve de sitio —
       `ponerElConteo()` lo busca dentro de `.vac-main`— pero deja de ser un
       párrafo suelto.
     · El estado vacío deja de ser un renglón varado en mitad de la nada.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── El héroe editorial no es el título de una pantalla ──────────────────── */
html.dm-app-mode body .vac-hero { display: none !important; }

/* ── El título grande, con el orden a su lado ────────────────────────────── */
html.dm-app-mode .dm-vac-cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
}
html.dm-app-mode .dm-vac-cabecera h1 { flex: 1 1 auto; min-width: 0; }

/* El orden estaba alineado al borde derecho de la pantalla, sin forma y con
   «Más recientes» tocando el canto. Aquí es un control: pastilla de relleno,
   galón propio y la zona de toque completa. */
html.dm-app-mode .dm-vac-orden {
  position: relative;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  border-radius: var(--r-max);
  background: var(--c-relleno);
  color: var(--c-label);
  transition: background-color var(--t-tap) var(--sal);
}
html.dm-app-mode .dm-vac-orden:active { background: var(--c-relleno-2); }
html.dm-app-mode .dm-vac-orden .sort-sel {
  appearance: none;
  -webkit-appearance: none;
  min-height: var(--toque);
  /* El hueco de la derecha es para el galón. */
  padding: 0 var(--s-7) 0 var(--s-4);
  border: 0;
  background: transparent;
  color: inherit;
  font: var(--w-medium) var(--t-subhead)/1 var(--f-ui);
  letter-spacing: -.005em;
}
/* El galón. `currentColor` y no un SVG incrustado: así sigue al esquema sin
   que nadie tenga que acordarse de duplicar el color en el bloque oscuro. */
html.dm-app-mode .dm-vac-orden::after {
  content: '';
  position: absolute;
  right: var(--s-4);
  width: 7px;
  height: 7px;
  border: solid currentColor;
  border-width: 0 1.5px 1.5px 0;
  transform: translateY(-2px) rotate(45deg);
  opacity: .5;
  pointer-events: none;
}

/* ── Un campo de búsqueda, no un formulario ──────────────────────────────── */
/* La tarjeta que envolvía la barra se va: una tarjeta dentro de la pantalla
   que ya es la tarjeta no separa nada, sólo añade un borde y una sombra. */
html.dm-app-mode body .search-block {
  padding: var(--s-2) var(--margen) var(--s-4);
  border-bottom: 0;
  background: transparent;
}
html.dm-app-mode body .search-bar {
  display: flex;
  /* `responsive.css` apila los tres controles por debajo de 768 px con
     `flex-direction: column !important`, y de ahí salían los 187 px de alto.
     Se mide, no se deduce: la columna no estaba en la hoja de la página ni en
     `app-mode.css`, que son los dos sitios donde se busca primero. */
  flex-direction: row !important;
  align-items: stretch;
  max-width: none;
  min-height: var(--toque);
  border-radius: var(--r-2);
  background: var(--c-relleno);
  box-shadow: none;
  /* El aro de foco del armazón es `outline: 3px` con 2 px de separación y un
     halo de 5 px, y `responsive.css` recorta la barra con `overflow: hidden
     !important`. Recortado, de los cuatro lados del aro sólo se veía el que
     cae DENTRO de la barra: una raya negra de 3 px a media altura, que parecía
     un separador puesto donde no toca. Aquí no hay nada que recortar —los dos
     campos son transparentes y el relleno lo pone la barra— así que el aro se
     deja salir entero, que es lo que hace que se entienda como foco. */
  overflow: visible !important;
  transition: background-color var(--t-tap) var(--sal);
}
html.dm-app-mode body .search-bar:focus-within { background: var(--c-relleno-2); }

/* La lupa se dibuja con un borde y una línea, sin traer un archivo ni un
   glifo de una tipografía que puede no haber llegado todavía. */
html.dm-app-mode body .search-bar { position: relative; }
html.dm-app-mode body .search-bar::before,
html.dm-app-mode body .search-bar::after {
  content: '';
  position: absolute;
  left: var(--s-4);
  pointer-events: none;
  color: var(--c-label-3);
}
html.dm-app-mode body .search-bar::before {
  top: 50%;
  width: 11px;
  height: 11px;
  margin-top: -8px;
  border: 1.6px solid currentColor;
  border-radius: 50%;
}
html.dm-app-mode body .search-bar::after {
  top: 50%;
  width: 1.6px;
  height: 5px;
  margin: 1px 0 0 9px;
  border-radius: 1px;
  background: currentColor;
  transform: rotate(-45deg);
  transform-origin: top center;
}

/* Los tres `!important` de aquí abajo no son pereza: `responsive.css` da a
   los dos campos `width: 100% !important`, `padding: 15px 18px !important` y
   —lo que se veía como una raya suelta cruzando la barra— un
   `border-bottom !important` por campo, que en columna eran los separadores
   entre pisos y en fila es un subrayado a media altura. Sin ganar esas tres
   declaraciones no hay carril: el hueco de la lupa lo pisaba el texto. */
html.dm-app-mode body .search-input {
  flex: 1 1 auto;
  width: auto !important;
  min-width: 0;
  /* El hueco de la izquierda es el de la lupa. */
  padding: var(--s-3) var(--s-3) var(--s-3) var(--s-8) !important;
  border-bottom: 0 !important;
  font-size: var(--t-callout) !important;
  font-weight: var(--w-regular);
  font-family: var(--f-ui);
  line-height: 1.2;
  color: var(--c-label);
}
html.dm-app-mode body .search-loc {
  flex: 0 0 140px;
  width: auto !important;
  min-width: 0;
  padding: var(--s-3) var(--s-4) !important;
  border-bottom: 0 !important;
  font-size: var(--t-callout) !important;
  font-weight: var(--w-regular);
  font-family: var(--f-ui);
  line-height: 1.2;
  color: var(--c-label);
}
html.dm-app-mode body :is(.search-input, .search-loc)::placeholder { color: var(--c-placeholder); }
/* El aro de foco sigue al `border-radius` del elemento que lo lleva, y
   `responsive.css` deja los dos campos en `border-radius: 0 !important` —de
   cuando eran tres pisos de una tarjeta—, así que el aro salía en ángulo recto
   sobre una barra redondeada. */
html.dm-app-mode body :is(.search-input, .search-loc) { border-radius: var(--r-2) !important; }
/* El separador vuelve, en vertical: es lo que dice que son dos campos y no uno
   con el texto raro. `responsive.css` lo apagaba porque en columna sobraba. */
html.dm-app-mode body .search-sep {
  display: block !important;
  flex: 0 0 1px;
  width: 1px;
  margin: var(--s-3) 0;
  background: var(--c-separador);
}
/* Enter ya buscaba desde los dos campos; el botón sobraba y costaba 50 px. */
html.dm-app-mode body .search-btn { display: none; }

/* ── El carril de filtros llega a los dos bordes ─────────────────────────── */
html.dm-app-mode body .filter-row {
  display: block;
  padding: 0;
  border-bottom: 0;
}
html.dm-app-mode body .filter-chips {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-1) var(--margen) var(--s-4);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  /* Para que el primer chip se alinee con el título y el último entre
     completo en vez de quedarse pegado al canto. */
  scroll-padding-inline: var(--margen);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
html.dm-app-mode body .filter-chips::-webkit-scrollbar { display: none; }
html.dm-app-mode body .filter-chips > * { flex: 0 0 auto; scroll-snap-align: start; }
/* Un filtro es un control secundario: 15 pt, no los 17 del cuerpo. El alto
   sigue siendo el de un dedo —lo pone la regla de 44 pt del armazón— pero
   dejan de leerse como siete acciones principales en fila. */
html.dm-app-mode body .filter-chips .fchip {
  padding: 0 var(--s-4);
  border-radius: var(--r-max);
  font: var(--w-medium) var(--t-subhead)/1 var(--f-ui);
  letter-spacing: -.005em;
}
/* «FILTRAR:» ocupaba 48 px del carril para decir lo que los chips ya dicen.
   Lo que aportaba —nombrar el grupo— vive ahora en el `aria-label` del
   contenedor, así que no se pierde para quien no ve la pantalla. */
html.dm-app-mode body .fchip-lbl { display: none; }
html.dm-app-mode body .row-sep { display: none; }

/* ── El contador es un encabezado de sección ─────────────────────────────── */
html.dm-app-mode body .vac-main { padding: 0 var(--margen) var(--s-8); }
html.dm-app-mode body .vac-topbar { margin: 0 0 var(--s-3); }
html.dm-app-mode body .vac-count {
  margin: 0;
  font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui);
  color: var(--c-label-2);
}
html.dm-app-mode body .vac-count strong { font-weight: var(--w-semibold); color: var(--c-label); }

/* ── Cuando no hay nada que enseñar ──────────────────────────────────────── */
/* Antes: un renglón de 13 px con 80 px de aire arriba y abajo, centrado a lo
   ancho y varado a media pantalla, sin decir qué hacer. Los estilos iban en
   el atributo `style`, así que ninguna hoja podía vestirlo: se sacaron a una
   clase en `vacantes.html`, que es lo que hace posible esto. */
html.dm-app-mode body ._empty {
  display: grid;
  justify-items: center;
  gap: var(--s-2);
  margin: 0;
  padding: var(--s-8) var(--s-5);
  font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui);
  color: var(--c-label-2);
  text-wrap: pretty;
  max-width: 30ch;
  margin-inline: auto;
}
html.dm-app-mode body ._empty.es-fallo { color: var(--c-error); }
html.dm-app-mode body ._empty-titulo {
  margin: 0;
  font: var(--w-semibold) var(--t-headline)/var(--t-headline-lh) var(--f-ui);
  letter-spacing: -.011em;
  color: var(--c-label);
}
html.dm-app-mode body ._empty-btn {
  margin-top: var(--s-4);
  min-height: var(--toque);
  padding: 0 var(--s-6);
  border: 0;
  border-radius: var(--r-max);
  background: var(--c-relleno);
  color: var(--c-label);
  font: var(--w-semibold) var(--t-subhead)/1 var(--f-ui);
  letter-spacing: -.005em;
}

/* La equis de vaciar el campo.
   ───────────────────────────────────────────────────────────────────────────
   Viene con `type="search"`, y sin tocarla Chromium la pinta como una equis
   azul de sistema en mitad de una barra color hueso. Se enmascara en vez de
   incrustarse como imagen: la máscara sólo aporta la FORMA y el color lo pone
   `background-color`, así que sigue al esquema sin un segundo archivo ni un
   color escrito a mano que haya que acordarse de duplicar en oscuro. */
html.dm-app-mode body :is(.search-input, .search-loc)::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  width: 15px;
  height: 15px;
  margin-left: var(--s-2);
  background-color: var(--c-label-4);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 4 L12 12 M12 4 L4 12' stroke='%23000' stroke-width='2' stroke-linecap='round'/%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 16 16'%3E%3Cpath d='M4 4 L12 12 M12 4 L4 12' stroke='%23000' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
  cursor: pointer;
}

/* ── La tarjeta ──────────────────────────────────────────────────────────── */
/* La hoja de la web le da a la fila `border-radius:14px 14px 14px 0` —la
   esquina recortada de la marca, que en la web es una firma y en una lista de
   diez tarjetas seguidas es una muesca que se repite diez veces— y un borde
   de 1 px. En la app la lista ya vive sobre el fondo agrupado, así que basta
   la superficie elevada. */
html.dm-app-mode body .vrow {
  border: 0;
  border-radius: var(--r-3);
  margin-bottom: var(--s-3);
  padding: var(--s-5) !important;
  gap: var(--s-2) !important;
  box-shadow: inset 0 0 0 .5px var(--c-separador);
}
html.dm-app-mode body .vcol-titulo {
  font: var(--w-semibold) var(--t-title-3)/var(--t-title-3-lh) var(--f-ui);
  letter-spacing: -.012em;
  color: var(--c-label);
}

/* La tarjeta pasa de seis renglones a cuatro.
   ───────────────────────────────────────────────────────────────────────────
   Ubicación, empresa, modalidad y jornada son cuatro datos cortos y cada uno
   ocupaba una línea entera: 253 px por tarjeta, o sea dos vacantes por
   pantalla. La rejilla que los empareja YA existe —`@media(min-width:769px)`
   la usa para la vista de escritorio— pero sus `grid-area` viven dentro de esa
   media query, así que por debajo de 769 px no se aplican y el bloque de 768
   deja `grid-template-columns:1fr`, que es una columna y seis filas.

   Aquí se declaran para el móvil las mismas áreas, con dos columnas, y con
   ellas vuelve a tener sentido el punto medio que separa los datos. */
html.dm-app-mode body .vrow {
  grid-template-columns: auto minmax(0, 1fr) !important;
  grid-template-areas:
    'puesto   puesto'
    'ubic     empresa'
    'tags     jornada'
    'acciones acciones' !important;
  row-gap: var(--s-2) !important;
  column-gap: var(--s-3) !important;
  align-items: center;
}
html.dm-app-mode body .vrow > .vcol-puesto { grid-area: puesto; }
/* `nth-child` y no `nth-of-type`, por lo mismo que anota la hoja de la página:
   el primer div es `.vcol-puesto`, así que contando por tipo la ubicación y la
   empresa salen cambiadas. */
html.dm-app-mode body .vrow > .vcol:nth-child(2) { grid-area: ubic; }
html.dm-app-mode body .vrow > .vcol:nth-child(3) { grid-area: empresa; }
html.dm-app-mode body .vrow > .vcol-tags { grid-area: tags; }
html.dm-app-mode body .vrow > .vcol-modal { grid-area: jornada; justify-self: start; }
html.dm-app-mode body .vrow > .vactions { grid-area: acciones; }
html.dm-app-mode body .vrow > .vcol:nth-child(3)::before {
  content: '·';
  margin-right: var(--s-3);
  color: var(--c-label-3);
}
html.dm-app-mode body .vrow :is(.vcol, .vcol-modal) {
  font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui);
  color: var(--c-label-2);
}
html.dm-app-mode body .vactions { margin-top: var(--s-2); gap: var(--s-2); }
/* «♡ GUARDAR» y «VER VACANTE» en versalitas con un cuarto de em de
   entreletra: eso es un rótulo de sección de la web, no un botón de app. El
   texto que escribe `buildRow()` ya es «♡ Guardar»; lo que lo gritaba era el
   `text-transform` de la hoja. */
html.dm-app-mode body :is(.btn-save-job, .btn-ver-vac) {
  min-height: var(--toque);
  height: auto;
  border-radius: var(--r-2);
  font: var(--w-semibold) var(--t-subhead)/1 var(--f-ui);
  letter-spacing: -.005em;
  text-transform: none;
}
html.dm-app-mode body .btn-save-job {
  background: var(--c-relleno);
  border-color: transparent;
  color: var(--c-label);
}
html.dm-app-mode body .btn-ver-vac { background: var(--c-primario); color: var(--c-bg); }



/* ═══════════════════════════════════════════════════════════════════════════
   45 · El perfil público: la ficha, no la página
   ───────────────────────────────────────────────────────────────────────────
   La sección 36 dejó el perfil del PANEL como una ficha de app. El perfil
   público —`/perfil/:slug`, el que ve cualquiera y el que se comparte— quedó
   fuera, y ahí siguen las tres cosas que se ven en cuanto se abre.

   NOTA · Esto se escribió cuando el HTML de las dos pantallas no era el mismo:
   el panel escribía `.stat-recom` + `.stat-conexiones` y el público
   `.dm-cc-cifras` con `.recom-number` + `.recom-label`. Dos formas para el
   mismo dato, y por eso la 36 no podía alcanzar al público.

   Ya están unificadas: `perfil-integration.js` escribe la forma del panel y la
   36 cubre `:is(#page-mi-perfil, .profile-page)`. Lo que queda aquí abajo es
   sólo lo que de verdad es distinto —el perfil público tiene CINCO acciones y
   el panel dos—, no una segunda copia del mismo diseño.

   ── 1. «0  RECOMENDACIONES   0 · CONEXIONES» ──────────────────────────────
   Medido, no mirado. La línea de cifras traía dos restos de cuando esto se
   apilaba en dos renglones:

     .stat-conexiones   border-bottom: 1px rgba(0,0,0,.1)  ·  padding-bottom: 9px
     .stat-con-dot      3 × 3 px, #ccc, redondo, en medio de «0» y «Conexiones»

   El borde separaba dos filas que ya no existen: en una sola línea es una raya
   de 105 px que empieza y termina en la nada, debajo de una sola de las dos
   cifras. Y el punto separaba número de rótulo cuando estaban en renglones
   distintos; en línea queda entre ellos, agrupando al revés de como se lee.

   Los rótulos van en versalitas con 1.2 px de entreletra a 12 px. Eso es un
   rótulo de sección de una web, y aquí son la unidad de una cifra: «0
   recomendaciones». En el sistema esto es texto de nota al pie, en redonda.

   ── 2. La fila de acciones mezclaba formas ────────────────────────────────
   Tres círculos de 44 con borde de pelo y una pastilla de 168 × 44 con radio
   20. El radio 20 sobre 44 de alto no es ni un círculo ni un rectángulo: es
   una cuarta forma en una fila que ya tenía dos. Y los tres círculos, con su
   borde de 1 px y fondo transparente, pesan menos que el hueco que los separa.
   En el sistema un control secundario es relleno sin borde.

   ── 3. Las flechas de las pestañas ────────────────────────────────────────
   Se arreglan arriba, extendiendo la regla de la sección 36 en vez de
   escribirla otra vez.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── La línea de cifras: ya no vive aquí ──────────────────────────────────
   Estaba escrita en esta sección porque el perfil público usaba otros nombres
   —`.dm-cc-cifras` con `.recom-number` / `.recom-label`— y la sección 36 no
   podía alcanzarlo. Esa era la deuda que el encabezado de arriba dejaba
   apuntada: «unificarlas es otra tarea».

   Hecha. `perfil-integration.js` escribe ahora `.stat-recom` y
   `.stat-conexiones` igual que el panel, la 36 se amplió a `.profile-page`, y
   estas nueve reglas pasaron a ser una segunda versión del mismo diseño. Dos
   sitios donde cambiar lo mismo es cómo se separaron la primera vez. */

/* ── La fila de acciones, una sola gramática ─────────────────────────────── */
html.dm-app-mode body .profile-page :is(.save-btn, .dm-share-btn, .dm-report-btn) {
  border: 0;
  border-radius: var(--r-max);
  background: var(--c-relleno);
  color: var(--c-label);
}
html.dm-app-mode body .profile-page :is(.save-btn, .dm-share-btn, .dm-report-btn):active {
  background: var(--c-relleno-2);
}
/* Radio 20 sobre 44 de alto era una forma propia. La acción principal comparte
   la de los tres controles que tiene al lado. */
html.dm-app-mode body .profile-page :is(.contact-btn, .dm-edit-btn) {
  min-height: var(--toque);
  border-radius: var(--r-max);
  font: var(--w-semibold) var(--t-subhead)/1 var(--f-ui);
  letter-spacing: -.005em;
  text-transform: none;
}

/* ── Las pestañas y los rótulos dejan de gritar ──────────────────────────── */
/* Versalitas con entreletra ancha en la fila de pestañas: eso es un índice de
   revista. Una pestaña del sistema es texto en redonda, y el peso es lo que
   dice cuál está activa. */
/* Las pestañas se definen arriba, en la 36, para las DOS pantallas. Aquí
   estaban otra vez y con otra escala —15 px contra los 12 del panel—, que es
   cómo se separaron. */
html.dm-app-mode body .profile-page :is(.section-title, .info-label) {
  font: var(--w-semibold) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui);
  letter-spacing: 0;
  text-transform: none;
  color: var(--c-label-2);
}


/* ═══════════════════════════════════════════════════════════════════════════
   46 · La primera pantalla, como la de una app
   ───────────────────────────────────────────────────────────────────────────
   Lo que se veía al abrir la app sin sesión, a 390×844:

     · Ni el nombre ni para qué sirve. La portada decía «Buenos días» y un
       botón; «Design Match» sólo aparecía en la letra pequeña del pie. Quien
       la instaló por recomendación no tenía dónde leer qué era.
     · Una tarjeta flotando con 16 px de navy por los cuatro lados. Una hoja
       de sistema no flota: sale del borde de abajo.
     · Crear una cuenta, que es lo que viene a hacer quien abre la app por
       primera vez, era un enlace dentro de una frase.
     · Los campos llevaban la etiqueta metida en el borde, al estilo Material,
       con un parche de fondo que no era el del campo. Y al enfocarse
       pintaban dos anillos: el del foco global y el borde vino.

   Nada de esto cambia qué hace cada pantalla, sólo cómo se lee.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── La marca y para qué sirve ─────────────────────────────────────────────
   El logotipo es el mismo PNG de la cabecera —ya precacheado—: tinta en
   claro y blanco por filtro en oscuro. El lema es el del manifiesto de la
   app: no se inventa una frase nueva para la primera pantalla. */
html.dm-app-mode .dm-acc-marca {
  flex: none;
  margin: var(--s-5) 0 var(--s-6);
  padding: 0 var(--s-2);
}
html.dm-app-mode .dm-acc-logo {
  display: block;
  width: 136px;
  height: auto;
}
html.dm-app-mode .dm-acc-lema {
  max-width: 24ch;
  margin: var(--s-3) 0 0;
  font: var(--w-regular) var(--t-title-3)/1.3 var(--f-ui);
  letter-spacing: -.01em;
  color: var(--c-label-2);
  text-wrap: balance;
}
@media (prefers-color-scheme: dark) {
  html.dm-app-mode .dm-acc-logo { filter: brightness(0) invert(1); }
}

/* ── La hoja sale del borde de abajo ───────────────────────────────────────
   Márgenes negativos del mismo tamaño que el relleno del armazón: la hoja
   llega a los dos lados y al borde inferior, y la zona segura se le suma por
   dentro para que nada quede debajo de la barra de inicio. */
html.dm-app-mode .dm-acc-tarjeta {
  margin: 0 calc(-1 * max(16px, calc(16px + var(--dm-app-safe-right, 0px))))
    calc(-1 * (20px + var(--dm-app-safe-bottom, 0px)))
    calc(-1 * max(16px, calc(16px + var(--dm-app-safe-left, 0px))));
  padding: var(--s-7) max(var(--s-6), calc(var(--s-6) + var(--dm-app-safe-right, 0px)))
    calc(var(--s-6) + var(--dm-app-safe-bottom, 0px))
    max(var(--s-6), calc(var(--s-6) + var(--dm-app-safe-left, 0px)));
  border-radius: var(--r-5) var(--r-5) 0 0;
  box-shadow: var(--acc-hoja-sombra);
}

/* Las vistas con formulario, igual: una hoja grande que empieza arriba —el
   formulario tiene que quedar por encima del teclado— y llega al borde de
   abajo en vez de dejar una franja de navy debajo. */
html.dm-app-mode:not(.dm-acc-en-portada) #dm-app-auth-shell {
  display: flex;
  flex-direction: column;
}
html.dm-app-mode:not(.dm-acc-en-portada) .dm-app-auth-view:not([hidden]) {
  flex: 1 0 auto;
  width: auto;
  max-width: none;
  margin: calc(var(--s-8) + var(--s-6)) calc(-1 * max(16px, calc(16px + var(--dm-app-safe-right, 0px))))
    calc(-1 * (20px + var(--dm-app-safe-bottom, 0px)))
    calc(-1 * max(16px, calc(16px + var(--dm-app-safe-left, 0px))));
  padding: var(--s-7) max(var(--s-6), calc(var(--s-6) + var(--dm-app-safe-right, 0px)))
    calc(var(--s-7) + var(--dm-app-safe-bottom, 0px))
    max(var(--s-6), calc(var(--s-6) + var(--dm-app-safe-left, 0px)));
  border-radius: var(--r-5) var(--r-5) 0 0;
  box-shadow: var(--acc-hoja-sombra);
}
@media (max-height: 700px) {
  html.dm-app-mode:not(.dm-acc-en-portada) .dm-app-auth-view:not([hidden]) { margin-top: 72px; }
}

/* En una tableta la hoja a todo lo ancho mediría 800 px: ahí vuelve a ser una
   tarjeta centrada, con la marca encima alineada con ella. */
@media (min-width: 600px) {
  html.dm-app-mode .dm-acc-marca,
  html.dm-app-mode .dm-acc-tarjeta {
    width: min(100%, 440px);
    margin-inline: auto;
  }
  html.dm-app-mode .dm-acc-tarjeta {
    margin-bottom: 0;
    padding: var(--s-7) var(--s-6) var(--s-6);
    border-radius: var(--r-5);
  }
  html.dm-app-mode:not(.dm-acc-en-portada) .dm-app-auth-view:not([hidden]) {
    flex: 0 0 auto;
    width: min(100%, 440px);
    margin-inline: auto;
    margin-bottom: 0;
    padding: var(--s-7) var(--s-6);
    border-radius: var(--r-5);
  }
}

/* ── Entrar como entra una hoja ────────────────────────────────────────────
   La hoja sale del borde de abajo, así que sube desde ahí: 28 px con la curva
   de cajón, no el fundido de 9 px que traía la tarjeta flotante. La portada
   —marca y lema— sólo se funde: es el fondo, no lo que se mueve. Con
   movimiento reducido todo se queda en un fundido corto. */
@keyframes dmAccHoja { from { opacity: 0; transform: translateY(28px); } to { opacity: 1; transform: none; } }
@keyframes dmAccFundido { from { opacity: 0; } to { opacity: 1; } }
html.dm-app-mode .dm-app-auth-view.dm-acc-portada { animation: dmAccFundido 240ms ease-out both; }
html.dm-app-mode .dm-acc-tarjeta { animation: dmAccHoja 420ms cubic-bezier(.32, .72, 0, 1) both; }
html.dm-app-mode:not(.dm-acc-en-portada) .dm-app-auth-view:not([hidden]) {
  animation: dmAccHoja 360ms cubic-bezier(.32, .72, 0, 1) both;
}
@media (prefers-reduced-motion: reduce) {
  html.dm-app-mode :is(.dm-acc-tarjeta, .dm-app-auth-view.dm-acc-portada),
  html.dm-app-mode:not(.dm-acc-en-portada) .dm-app-auth-view:not([hidden]) {
    animation: dmAccFundido 160ms linear both;
  }
}

/* ── Crear cuenta es un botón ──────────────────────────────────────────────
   Secundario —relleno, sin sombra— y a la altura del principal, que es lo que
   dice «esto también es una manera de empezar». La Passkey baja a texto. */
html.dm-app-mode body .dm-acc-tarjeta .dm-acc-crear {
  width: 100%;
  min-height: 52px;
  margin-top: var(--s-3);
  border-radius: var(--r-3);
}
/* El hueco del mensaje de error sólo ocupa sitio cuando hay mensaje. */
html.dm-app-mode .dm-acc-tarjeta .dm-app-inline-error:empty { min-height: 0; }

/* ── Los campos: la etiqueta encima, un solo foco ──────────────────────────
   Como en los ajustes del sistema: la etiqueta es texto sobre el campo, no
   un recorte en su borde. El foco lo dice UNA cosa: el anillo de
   `dm-accessibility.css`, que está protegido con `!important` para que nadie
   deje sin rastro a quien navega con teclado. Lo que sobraba era el borde
   vino de 2 px que se le sumaba por dentro: dos anillos, uno dentro de otro. */
html.dm-app-mode #dm-app-auth-shell .dm-app-field {
  display: grid;
  gap: var(--s-2);
  padding-top: 0;
}
html.dm-app-mode #dm-app-auth-shell .dm-app-field > label {
  position: static;
  padding: 0;
  background: none;
  font: var(--w-semibold) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui);
  color: var(--c-label-2);
}
html.dm-app-mode #dm-app-auth-shell .dm-app-field:focus-within > label { color: var(--c-label); }
html.dm-app-mode #dm-app-auth-shell .dm-app-field input,
html.dm-app-mode #dm-app-auth-shell .dm-app-field select {
  /* La familia, dicha: el panel pone DM Sans a todo `input` con (0,1,2), más
     que el `.dm-app-field input` de la hoja base, y el correo y la contraseña
     salían en otra letra justo en la pantalla con la que arranca la app. */
  font-family: var(--f-ui);
  min-height: 52px;
  padding: 14px 15px;
  border: 1px solid var(--c-separador);
  border-radius: var(--r-2);
  background: var(--c-relleno);
  transition: border-color 160ms var(--sal), box-shadow 160ms var(--sal);
}
html.dm-app-mode #dm-app-auth-shell .dm-app-field input:focus,
html.dm-app-mode #dm-app-auth-shell .dm-app-field select:focus {
  border-width: 1px;
  padding-inline: 15px;
  border-color: var(--c-separador);
  box-shadow: none;
  background: var(--c-elevado);
}
html.dm-app-mode #dm-app-auth-shell .dm-app-field .dm-app-password-wrap input { padding-right: 64px; }
/* La casilla de los Términos no tiene clase propia: sin esto tomaba la letra
   que el panel da a todo `input` y el interlineado de la página anfitriona. */
html.dm-app-mode #dm-app-auth-shell .dm-app-check input { font: inherit; }
html.dm-app-mode #dm-app-auth-shell .dm-app-password-toggle {
  font: var(--w-semibold) var(--t-footnote)/1 var(--f-ui);
  color: var(--c-tinte);
}
@media (prefers-reduced-motion: reduce) {
  html.dm-app-mode #dm-app-auth-shell .dm-app-field :is(input, select) { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   47 · Las versalitas que quedaban, y la raya de «lo siguiente»
   ───────────────────────────────────────────────────────────────────────────
   Un escáner recorrió las ocho pantallas del panel en la app buscando texto
   con `text-transform: uppercase` o entreletra ancha. Salieron 46 elementos:
   35 rótulos —«FALTA TÍTULO PROFESIONAL», «COMPLETAR», «VER MÁS», «TU PLAN
   ACTUAL», «ACTIVA», «EDITAR», «CAMBIAR», «SIEMPRE ACTIVOS»…— y los once
   números «01 … 11» de las categorías del editor. Texto de interfaz gritado,
   que en un teléfono se lee más despacio y no dice nada que la caja normal
   no diga.

   Aquí se apagan todas de una vez, en el tamaño y el color de la escala. Las
   que llevan el estilo escrito en línea (`pabName`, el bloque de cancelar)
   necesitan `!important`: es la única manera de ganarle a un `style=`.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body :is(.dm-nba-porque, .dm-di-porque, .dm-di-motivo, .sp-tag,
  /* Las etiquetas de las hojas de Inicio: disponibilidad y completar el perfil. */
  .dm-disp-etiqueta,
  .info-label, .edit-topbar-sub, .section-eyebrow, #edit-category-label, #edit-category-label span, .pp-eyebrow,
  .pab-label, .pp-refmore, .ptoggle-save, .pp-fundador-label, .plan-current-badge,
  .cfg-siempre, .cfg-estado-texto, .dm-profile-type-badge) {
  text-transform: none !important;
  letter-spacing: 0 !important;
}
html.dm-app-mode body :is(.dm-nba-boton, .dm-di-todo, .sp-ver, .ptoggle-btn,
  .pp-cta, .cfg-btn-edit, .edit-nav-item, .edit-nav-item span, .dm-mp-add, .dm-disp-guardar) {
  text-transform: none !important;
  letter-spacing: 0 !important;
}
/* Los estados del plan, que el panel escribe en línea con versalitas. */
html.dm-app-mode body .pab-name span,
html.dm-app-mode body #dm-cancel-sub-block :is(p, button) {
  text-transform: none !important;
  letter-spacing: 0 !important;
}

/* Los rótulos que dicen POR QUÉ: nota al pie, no titular. */
html.dm-app-mode body :is(.dm-nba-porque, .pab-label, .pp-eyebrow, .section-eyebrow) {
  font: var(--w-semibold) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui) !important;
}
html.dm-app-mode body :is(.dm-di-porque, .dm-di-motivo) {
  font: var(--w-regular) var(--t-caption-1)/var(--t-caption-1-lh) var(--f-ui) !important;
}
/* Las hojas de Inicio: sin versalitas, la etiqueta es una nota al pie y el
   botón se lee al tamaño de un botón, no al de un rótulo. */
html.dm-app-mode body .dm-disp-etiqueta {
  font: var(--w-medium) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui) !important;
}
html.dm-app-mode body .dm-disp-guardar {
  font: var(--w-semibold) var(--t-subhead)/1.2 var(--f-ui) !important;
}
/* «Ver más» en el color de lo que se toca, a su tamaño de texto. */
html.dm-app-mode body .dm-di-todo {
  font: var(--w-medium) var(--t-subhead)/1.2 var(--f-ui) !important;
  color: var(--c-tinte) !important;
}

/* Los números de categoría («01» … «11») no ordenan nada: la lista no es una
   secuencia, es un menú. */
html.dm-app-mode body .cat-opt-num { display: none !important; }
/* Y sin el número, la rejilla se queda con una columna. La página la define
   de dos —34 px para el número y el resto—, y al esconderlo el nombre y la
   descripción caían en la de 34 px: «Diseñ / o, / Brand / ing &…», con la
   fila vacía al lado. Reportado con captura desde la app. Van juntas: quitar
   el número sin esto rompe la lista. */
html.dm-app-mode body .cat-opt-left { grid-template-columns: minmax(0, 1fr) !important; }

/* ── «Lo siguiente que hacer», sin la raya ─────────────────────────────────
   Era una línea vertical de 2 px a la izquierda: el recurso de subrayar una
   cita en una revista, y la raya lateral de color es justo lo que una
   interfaz de sistema no usa. Ahora es un bloque con relleno, que se separa
   de las tarjetas de debajo por el fondo, no por un borde. */
html.dm-app-mode body .dm-nba-carta {
  border-left: 0 !important;
  padding: var(--s-5) var(--s-5) var(--s-4) !important;
  border-radius: var(--r-3);
  background: var(--c-relleno);
}
html.dm-app-mode body .dm-nba-porque { color: var(--c-tinte) !important; margin: 0 0 var(--s-2) !important; }
html.dm-app-mode body .dm-nba-boton {
  min-height: var(--toque);
  padding: 0 var(--s-6) !important;
  border-radius: var(--r-max);
  font: var(--w-semibold) var(--t-subhead)/1 var(--f-ui) !important;
}
html.dm-app-mode body .dm-nba-otras { margin-left: 0 !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   48 · Configuración, como los ajustes del teléfono
   ───────────────────────────────────────────────────────────────────────────
   En el teléfono cada ajuste era una columna: la etiqueta, el valor y debajo
   un botón de lado a lado con «EDITAR» en versalitas. Tres ajustes de cuenta
   medían 840 px. La web apila así para que el botón no se quede sin sitio;
   en la app el patrón es el de Ajustes: la etiqueta y su valor a la
   izquierda, la acción al final de la misma fila, en el color de lo que se
   toca.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body .cfg-card {
  border: 0;
  border-radius: var(--r-3);
  background: var(--c-elevado);
  box-shadow: 0 0 0 .5px var(--c-separador);
}
html.dm-app-mode body :is(.cfg-row, .cfg-toggle-row) {
  flex-direction: row !important;
  flex-wrap: wrap;
  align-items: center !important;
  column-gap: var(--s-4) !important;
  row-gap: var(--s-2) !important;
  min-height: var(--toque);
  padding: var(--s-3) var(--s-5);
  border-bottom: .5px solid var(--c-separador);
}
/* El texto se queda con la fila mientras le quepan 12 rem; si lo de la
   derecha no cabe al lado, baja debajo en vez de estrujar la frase. */
html.dm-app-mode body .cfg-row-info { flex: 1 1 12rem; min-width: 0; }
/* El estado de la cuenta lleva icono, nombre, pastilla y frase: su botón va
   siempre debajo, alineado con el texto. */
html.dm-app-mode body .cfg-row:has(#cfg-estado-cta) > div:first-child { flex: 1 1 100%; }
html.dm-app-mode body #cfg-estado-cta {
  margin-left: calc(44px + 14px) !important;
  letter-spacing: 0 !important;
  font: var(--w-semibold) var(--t-subhead)/1 var(--f-ui) !important;
}
html.dm-app-mode body :is(.cfg-row, .cfg-toggle-row):last-child { border-bottom: 0; }
html.dm-app-mode body .cfg-row-label {
  margin: 0;
  font: var(--w-regular) var(--t-body)/var(--t-body-lh) var(--f-ui);
  color: var(--c-label);
}
html.dm-app-mode body :is(.cfg-row-val, .cfg-row-sub) {
  margin: 2px 0 0;
  font: var(--w-regular) var(--t-subhead)/var(--t-subhead-lh) var(--f-ui);
  color: var(--c-label-2);
  overflow-wrap: anywhere;
}

/* La acción: texto en el color de lo que se toca, con el cheurón de «esto
   lleva a otra pantalla». Sin borde, sin relleno y sin versalitas. */
html.dm-app-mode body .cfg-btn-edit {
  width: auto !important;
  flex: 0 0 auto;
  gap: var(--s-1);
  margin-right: calc(var(--s-2) * -1);
  padding: 0 var(--s-2);
  border: 0 !important;
  background: none;
  font: var(--w-regular) var(--t-body)/1 var(--f-ui);
  color: var(--c-tinte) !important;
}
html.dm-app-mode body .cfg-btn-edit::after {
  content: "";
  width: 7px;
  height: 7px;
  margin-left: 2px;
  border-top: 1.6px solid currentColor;
  border-right: 1.6px solid currentColor;
  transform: rotate(45deg);
  opacity: .6;
}
/* Eliminar la cuenta es destructivo: rojo, y sin cheurón que lo haga parecer
   un paso más. El rojo del sistema de la app, no el escrito en línea. */
html.dm-app-mode body .cfg-btn-edit[data-cfg-pestana="stab-account"] { color: var(--c-error) !important; }
html.dm-app-mode body .cfg-btn-edit[data-cfg-pestana="stab-account"]::after { content: none; }

/* «Siempre activos»: un estado, no un control. Texto secundario, sin la
   bolita que parecía un interruptor apagado. */
html.dm-app-mode body .cfg-siempre {
  font: var(--w-regular) var(--t-subhead)/1.2 var(--f-ui);
  color: var(--c-label-2);
}
html.dm-app-mode body .cfg-siempre::before { content: none; }

/* El rótulo de cada grupo, encima de su lista y fuera de ella. */
html.dm-app-mode body .cfg-sec-title {
  font: var(--w-regular) var(--t-title-3)/var(--t-title-3-lh) var(--f-display);
  color: var(--c-label);
}
html.dm-app-mode body .cfg-sec-desc {
  font: var(--w-regular) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui);
  color: var(--c-label-2);
}


/* ═══════════════════════════════════════════════════════════════════════════
   49 · El recorrido guiado, a la escala de la app
   ───────────────────────────────────────────────────────────────────────────
   Es lo primero que ve una cuenta nueva después del código de correo, y
   estaba escrito a la escala del escritorio: «PASO 1 DE 3» y «EMPEZAR» en
   versalitas de 11 px con entreletra ancha, el párrafo en 12.5 px de peso
   300. En un teléfono eso se lee entornando los ojos. Aquí sube a la escala
   de la app; la forma de hoja de los botones y de la burbuja —la esquina
   recta abajo— es de la marca y se queda.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body .dmt-paso {
  font: var(--w-semibold) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui);
  letter-spacing: 0;
  text-transform: none;
}
html.dm-app-mode body .dmt-titulo {
  font: var(--w-regular) var(--t-title-3)/var(--t-title-3-lh) var(--f-display);
}
html.dm-app-mode body .dmt-texto {
  font: var(--w-regular) var(--t-subhead)/1.5 var(--f-ui);
}
html.dm-app-mode body .dmt-btn {
  min-height: var(--toque);
  padding: 0 var(--s-5);
  font: var(--w-semibold) var(--t-subhead)/1 var(--f-ui);
  letter-spacing: 0;
  text-transform: none;
}
html.dm-app-mode body .dmt-btn--atras { padding: 0 var(--s-4); }
/* «Saltar recorrido» es un botón suelto, no un enlace en una frase. */
html.dm-app-mode body .dmt-saltar {
  font: var(--w-regular) var(--t-subhead)/1 var(--f-ui);
  text-decoration: none;
}

/* ═══════════════════════════════════════════════════════════════════════════
   50 · La barra del editor, como la de una app
   ───────────────────────────────────────────────────────────────────────────
   En el teléfono la barra de la web se apilaba entera: título, subtítulo,
   «Regresar», el estado, «Cancelar» y «Guardar cambios» a todo lo ancho, uno
   debajo de otro. 640 px de 844 antes de la primera pestaña del editor.

   Aquí es la barra de navegación de siempre: volver a la izquierda, guardar a
   la derecha, el título debajo y el estado en una línea pequeña. «Cancelar»
   se esconde porque hacía lo mismo que «Regresar» —ninguno de los dos
   descarta nada: lo escrito se conserva al salir—. Sin sticky: la barra de
   abajo ya lleva «Continuar a…», que también guarda.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode body .edit-topbar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "volver guardar" "titulo titulo" "estado estado";
  align-items: center;
  column-gap: var(--s-4);
  row-gap: 2px;
  padding: var(--s-2) var(--margen) var(--s-4);
}
html.dm-app-mode body .edit-topbar-actions { display: contents; }
html.dm-app-mode body .edit-topbar .edit-back {
  grid-area: volver;
  justify-self: start;
  margin-left: calc(var(--s-2) * -1);
  padding: 0 var(--s-2);
  font: var(--w-regular) var(--t-body)/1 var(--f-ui);
  letter-spacing: 0;
  color: var(--c-tinte) !important;
}
html.dm-app-mode body .edit-topbar .edit-back svg { width: 16px; height: 16px; stroke-width: 2; }
html.dm-app-mode body .edit-topbar .btn-ghost { display: none; }
html.dm-app-mode body .edit-topbar #embedded-profile-save {
  grid-area: guardar;
  flex: none;
  min-height: var(--toque);
  padding: 0 var(--s-5);
  border-radius: var(--r-max);
  font: var(--w-semibold) var(--t-subhead)/1 var(--f-ui);
  letter-spacing: 0;
}
html.dm-app-mode body .edit-topbar-center { grid-area: titulo; text-align: left; margin-top: var(--s-2); }
html.dm-app-mode body .edit-topbar-title {
  max-width: none;
  font: var(--w-regular) var(--t-title-3)/var(--t-title-3-lh) var(--f-display);
  color: var(--c-label);
}
html.dm-app-mode body .edit-topbar-sub { display: none; }
html.dm-app-mode body .edit-topbar .save-status {
  grid-area: estado;
  font: var(--w-regular) var(--t-footnote)/var(--t-footnote-lh) var(--f-ui);
}

/* La barra de abajo —«Continuar a…»— es opaca: el contenido pasa por debajo
   y no a través. Sin fondo propio, el número de sección y el botón quedaban
   escritos encima de la nota de la categoría. */
html.dm-app-mode body .edit-continue-wrap {
  background: var(--c-elevado);
  border-top: .5px solid var(--c-separador);
  box-shadow: 0 -8px 24px rgba(20, 20, 30, .06);
}
html.dm-app-mode body .edit-continue-wrap :is(.btn-navy, .edit-nav-prev-btn) {
  min-height: var(--toque);
  font: var(--w-semibold) var(--t-subhead)/1 var(--f-ui);
  letter-spacing: 0;
}
html.dm-app-mode body .edit-nav-pos-indicator {
  font: var(--w-regular) var(--t-footnote)/1 var(--f-ui);
  color: var(--c-label-2);
}
/* En el teléfono el «1 / 8» sobra —la fila de pestañas ya dice dónde se
   está— y le quitaba al botón el ancho que necesitaba: «Continuar a Carta de
   presentación →» salía en dos renglones. */
@media (max-width: 600px) {
  html.dm-app-mode body :is(.edit-nav-pos-indicator, .edit-continue-wrap > span:empty) { display: none; }
  html.dm-app-mode body .edit-continue-wrap .btn-navy { flex: 1 1 auto; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   51 · El fondo del acceso: el arte de la marca
   ───────────────────────────────────────────────────────────────────────────
   La mitad de arriba del acceso era navy con cuatro píldoras planas —azul,
   amarillo, lila y vino— a la deriva. Eran una imitación en CSS del arte de
   la portada web, sin su cristal, sin su grano y sin su luz, y el original ya
   existe y es de la marca.

   · El fondo es el de la apariencia: hueso en claro, tinta en oscuro. Es el
     mismo color que `theme-color` pone en la barra de estado, así que la
     pantalla empieza en el borde de arriba sin franja.
   · El arte es el JPEG del hero web sin su papel, compuesto dos veces: sobre
     blanco para claro y sobre casi negro para oscuro. `darken` borra el
     blanco contra el hueso y `lighten` el casi negro contra la tinta: sin
     recuadro y sin canal alfa, 39 KB y 24 KB. `<picture>` baja sólo uno.
   · Ocupa el sitio que sobra, no un alto fijo: en una pantalla baja o con la
     letra grande encoge, y la marca y la hoja no se mueven de donde se
     tocan. Si al arte le quedan menos de ~120 px —un iPhone SE—, no sale: un
     arte del tamaño de un icono es una mancha.
   · Flota al ritmo de la web —6,2 s por vaivén—, con menos recorrido porque
     aquí es más pequeño. Con movimiento reducido se queda quieto.
   ═══════════════════════════════════════════════════════════════════════════ */
html.dm-app-mode #dm-app-auth-shell {
  --acc-hoja-sombra: 0 0 0 .5px var(--c-separador), 0 -6px 24px rgba(43, 43, 66, .06);
  background: var(--c-bg);
}

html.dm-app-mode .dm-acc-arte {
  position: relative;
  flex: 1 1 0;
  min-height: 0;
  container-type: size;
  /* Un grupo propio con fondo opaco: la imagen se mezcla con el color de la
     pantalla y su halo, no con lo que haya detrás del fundido de entrada de
     la portada, que es transparente y dejaría ver el recuadro. */
  isolation: isolate;
  background: var(--c-bg);
}
/* El halo va en el `<picture>` y no en la caja: así, cuando no hay sitio para
   el arte, se va con él en vez de quedarse como una mancha suelta. */
html.dm-app-mode .dm-acc-arte picture {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle closest-side, rgba(255, 248, 211, .7), rgba(255, 248, 211, 0));
}
/* 16 px arriba y abajo: lo que sube y baja el arte al entrar y al flotar. Si
   asomara fuera de la caja, esa franja no tendría con qué mezclarse y se
   vería el borde de la imagen —blanco en claro, negro en oscuro—. */
html.dm-app-mode .dm-acc-arte img {
  position: absolute;
  inset: 16px 0;
  width: 100%;
  height: calc(100% - 32px);
  object-fit: contain;
  mix-blend-mode: darken;
  animation:
    dmAccArteEntra 900ms cubic-bezier(.22, .61, .36, 1) both,
    dmAccArteFlota 6.2s ease-in-out 900ms infinite alternate;
}
@container (max-height: 150px) {
  html.dm-app-mode .dm-acc-arte picture { display: none; }
}

/* La hoja no se estira: el sitio libre es del arte. */
html.dm-app-mode .dm-acc-portada .dm-acc-tarjeta { flex: none; }

/* Sube desde un poco más abajo y termina justo donde empieza el vaivén, para
   que el relevo entre las dos animaciones no se vea como un salto. */
@keyframes dmAccArteEntra {
  from { transform: translate3d(0, 14px, 0) scale(.96); }
  to { transform: translate3d(0, -6px, 0) scale(.998); }
}
@keyframes dmAccArteFlota {
  from { transform: translate3d(0, -6px, 0) scale(.998); }
  to { transform: translate3d(0, 0, 0) scale(1.002); }
}

@media (prefers-color-scheme: dark) {
  html.dm-app-mode #dm-app-auth-shell { --acc-hoja-sombra: 0 0 0 .5px var(--c-separador), 0 -8px 28px rgba(0, 0, 0, .35); }
  html.dm-app-mode .dm-acc-arte picture {
    background: radial-gradient(circle closest-side, rgba(219, 209, 148, .1), rgba(219, 209, 148, 0));
  }
  html.dm-app-mode .dm-acc-arte img { mix-blend-mode: lighten; }
}

@media (prefers-reduced-motion: reduce) {
  html.dm-app-mode .dm-acc-arte img { animation: none; }
}

/* En una tableta, a lo ancho de la columna de la marca y la hoja. */
@media (min-width: 600px) {
  html.dm-app-mode .dm-acc-arte {
    width: min(100%, 440px);
    margin-inline: auto;
  }
}

/* La cabecera de marca, en cualquier estado: cero superficie.
   Va al final del archivo a propósito — es la última palabra sobre el padre,
   y así ninguna regla de estado (scroll, cristal contextual, apariencia) puede
   devolverle una cápsula por descuido. */
html.dm-app-mode #dm-app-header.es-marca {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
