/* ==========================================================================
   qcrew design system
   --------------------------------------------------------------------------
   One file, no build step, no npm. Everything is driven by custom properties
   so a rebrand is a change to :root and nothing else.

   Dark is the default look. Light is a full re-declaration of the same tokens,
   applied either by the OS setting or by an explicit data-theme on <html>.
   ========================================================================== */

/* --------------------------------------------------------------- 1. Tokens */

:root {
    /* One accent, two neighbouring stops. A three-stop rainbow reads as noise
       at large sizes and fights the content; staying inside a single hue family
       keeps it modern without shouting. Used on the wordmark, primary buttons
       and active states — never on body text. */
    --brand-1: #5566f0;
    --brand-2: #7d5cf0;
    --brand-3: #5566f0;
    --gradient: linear-gradient(135deg, var(--brand-1) 0%, var(--brand-2) 100%);
    --gradient-soft: linear-gradient(135deg, rgb(85 102 240 / 10%), rgb(125 92 240 / 7%));

    /* Surfaces, dark by default. */
    --bg: #0d0f14;
    --bg-elevated: #14171e;
    --surface: #171a22;
    --surface-2: #1d212b;
    --surface-hover: #232833;
    --border: #262b36;
    --border-strong: #333947;

    --text: #eef0f4;
    --text-muted: #9aa1b1;
    --text-faint: #6a7183;

    --ok: #34d399;
    --warn: #fbbf24;
    --bad: #fb7185;
    /* Alarma de marca, para el nombre del tramadómetro. NO es un tono de
       estado: `--bad`/`--warn` van en par con su clase y no se usan de adorno. */
    --alarm: linear-gradient(120deg, #fb7185 0%, #fb923c 55%, #fbbf24 100%);
    --info: #60a5fa;

    /* Type. One family, three roles. */
    --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    --step--1: clamp(0.78rem, 0.76rem + 0.1vw, 0.84rem);
    --step-0: clamp(0.94rem, 0.9rem + 0.2vw, 1rem);
    --step-1: clamp(1.15rem, 1.05rem + 0.4vw, 1.35rem);
    --step-2: clamp(1.5rem, 1.3rem + 0.9vw, 2rem);
    --step-3: clamp(2rem, 1.6rem + 2vw, 3.25rem);
    --step-4: clamp(2.6rem, 1.9rem + 3.4vw, 4.5rem);

    --radius-sm: 8px;
    --radius: 14px;
    --radius-lg: 22px;
    --radius-full: 999px;

    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;
    --space-8: 4.5rem;

    --shadow-sm: 0 1px 2px rgb(0 0 0 / 40%);
    --shadow: 0 8px 24px -8px rgb(0 0 0 / 55%);
    --shadow-lg: 0 24px 60px -20px rgb(0 0 0 / 70%);
    --glow: 0 0 0 1px rgb(85 102 240 / 28%), 0 6px 24px -8px rgb(85 102 240 / 32%);

    --ease: cubic-bezier(0.22, 1, 0.36, 1);
    --speed: 180ms;

    --shell: 1200px;
}

/* Light theme. Redeclared in full so no colour is left borrowing a dark value. */
@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) {
        /* Más oscura: el rosado y el ámbar del oscuro no llegan a 3:1 sobre blanco. */
        --alarm: linear-gradient(120deg, #e11d48 0%, #ea580c 55%, #d97706 100%);
        --bg: #fbfbfd;
        --bg-elevated: #ffffff;
        --surface: #ffffff;
        --surface-2: #f4f4f8;
        --surface-hover: #eeeef4;
        --border: #e4e4ed;
        --border-strong: #d0d0dd;

        --text: #14141c;
        --text-muted: #5c5c72;
        --text-faint: #8e8ea3;

        --shadow-sm: 0 1px 2px rgb(20 20 28 / 6%);
        --shadow: 0 8px 24px -10px rgb(20 20 28 / 14%);
        --shadow-lg: 0 24px 60px -24px rgb(20 20 28 / 20%);
        --glow: 0 0 0 1px rgb(85 102 240 / 20%), 0 6px 22px -10px rgb(85 102 240 / 26%);
    }
}

:root[data-theme="light"] {
    --alarm: linear-gradient(120deg, #e11d48 0%, #ea580c 55%, #d97706 100%);
    --bg: #fbfbfd;
    --bg-elevated: #ffffff;
    --surface: #ffffff;
    --surface-2: #f4f4f8;
    --surface-hover: #eeeef4;
    --border: #e4e4ed;
    --border-strong: #d0d0dd;

    --text: #14141c;
    --text-muted: #5c5c72;
    --text-faint: #8e8ea3;

    --shadow-sm: 0 1px 2px rgb(20 20 28 / 6%);
    --shadow: 0 8px 24px -10px rgb(20 20 28 / 14%);
    --shadow-lg: 0 24px 60px -24px rgb(20 20 28 / 20%);
    --glow: 0 0 0 1px rgb(85 102 240 / 20%), 0 6px 22px -10px rgb(85 102 240 / 26%);
}

/* ---------------------------------------------------------------- 2. Reset */

*,
*::before,
*::after { box-sizing: border-box; }

* { margin: 0; }

html {
    color-scheme: dark light;
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

body {
    background: var(--bg);
    color: var(--text);
    font-family: var(--font);
    font-size: var(--step-0);
    line-height: 1.6;
    min-height: 100vh;
    /* Para los banners a todo el ancho (.hero-xl): se salen del shell con
       margenes en `vw`, y `vw` cuenta la barra de scroll. `clip` recorta ese
       sobrante sin crear contenedor de scroll — con `hidden` el header sticky
       dejaria de pegarse. */
    overflow-x: clip;
    -webkit-font-smoothing: antialiased;
    display: flex;
    flex-direction: column;
}

/* A single soft light source behind everything. Fixed so it does not
   parallax against the content while scrolling. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    background:
        radial-gradient(64rem 42rem at 14% -12%, rgb(85 102 240 / 7%), transparent 62%);
}

img, svg, video { display: block; max-width: 100%; height: auto; }

/* `hidden` tiene que significar oculto, siempre.

   El navegador trae `[hidden] { display: none }`, pero cualquier clase nuestra
   que declare un display le gana: las dos tienen la misma especificidad y las
   reglas del autor van por encima de las del navegador. `.alert` declara
   `display: flex`, asi que un `<div class="alert" hidden>` se veia igual — y
   `el.hidden = true` desde JS tampoco lo tapaba.

   Se descubrio en la pantalla de pago: el formulario de ONVO cargaba bien y
   encima quedaba el aviso de "no se pudo cargar", mas una caja de error vacia.
   `!important` es la unica forma de que una utilidad de una palabra gane sin
   depender del orden del archivo. */
[hidden] { display: none !important; }

input, button, textarea, select { font: inherit; color: inherit; }

a { color: inherit; text-decoration: none; }

h1, h2, h3, h4 { line-height: 1.15; font-weight: 700; letter-spacing: -0.02em; }
h1 { font-size: var(--step-3); }
h2 { font-size: var(--step-2); }
h3 { font-size: var(--step-1); }

p { text-wrap: pretty; }

:focus-visible {
    outline: 2px solid var(--brand-1);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

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

/* -------------------------------------------------------------- 3. Layout */

.shell {
    width: min(100% - 2rem, var(--shell));
    margin-inline: auto;
    position: relative;
    z-index: 1;
}

.shell--wide { --shell: 1440px; }
.shell--narrow { --shell: 780px; }

/* El panel de administración, más ancho que el sitio público.
   Sus tablas tienen ocho columnas y a 1200px pedían scroll horizontal PROPIO
   mientras sobraba pantalla a los lados: el ojo veía hueco y la mano tenía que
   arrastrar. La medida de lectura no aplica acá — esto no es prosa, es una
   tabla, y cortarla a 1200px no la hace más legible, la hace incompleta. */
.shell--app { --shell: 1760px; }

main { flex: 1 0 auto; padding-block: var(--space-6) var(--space-8); }
/* En móvil los 4,5rem de aire al fondo dejaban un hueco negro enorme entre el
   contenido —la paginación, sobre todo— y el pie. Con la pantalla angosta ese
   respiro sobra: el pie queda pegado a lo que cierra la página. */
@media (max-width: 40rem) { main { padding-bottom: var(--space-5); } }

.stack > * + * { margin-top: var(--space-4); }
.stack-lg > * + * { margin-top: var(--space-6); }

.row {
    display: flex;
    gap: var(--space-3);
    align-items: center;
    flex-wrap: wrap;
}

.row--between { justify-content: space-between; }
.row--end { justify-content: flex-end; }
.push { margin-left: auto; }

.grid { display: grid; gap: var(--space-4); }
.grid--cards { grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); }
.grid--wide  { grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr)); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
.grid--stats { grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr)); }

.section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
    flex-wrap: wrap;
}

.section-head h2 { font-size: var(--step-1); }

/* -------------------------------------------------------------- 4. Header */

.site-header {
    position: sticky;
    top: 0;
    z-index: 50;
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    backdrop-filter: blur(16px) saturate(160%);
    border-bottom: 1px solid var(--border);
}

.site-header__inner {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    height: 62px;
}

/* La fila de la derecha (idioma + tema, y en movil el menu) va en UNA sola linea.
   `.row` trae `flex-wrap: wrap`, y como el header mide 62px fijos, si algo no
   cupiera saltaria a una segunda linea que cae fuera de esos 62px y desaparece.
   En una pantalla angosta caben de sobra; esto solo evita el envolvimiento. */
.site-header__inner .push { flex-wrap: nowrap; }


.wordmark {
    font-size: 1.32rem;
    font-weight: 800;
    letter-spacing: -0.045em;
    background: var(--gradient);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    flex-shrink: 0;
}

.nav { display: flex; gap: var(--space-1); align-items: center; }

.nav a {
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-full);
    color: var(--text-muted);
    font-size: var(--step--1);
    font-weight: 550;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
    white-space: nowrap;
}

.nav a:hover { background: var(--surface-hover); color: var(--text); }
.nav a[aria-current="page"] { background: var(--surface-2); color: var(--text); }

/* En móvil la navegación, el idioma y la sesión se van a un menú desplegable
   detrás de un botón, en vez de la tira horizontal que se cortaba sin avisar
   que tenía scroll. En escritorio nada de esto existe: la barra queda igual. */

/* El envoltorio de lo que solo se ve en escritorio. `display:contents` hace que
   en escritorio desaparezca y sus hijos sigan siendo los mismos elementos flex
   de la barra — ni un pixel cambia. En móvil se oculta entero. */
.hdr-only { display: contents; }

/* El botón de menú. No existe en escritorio. */
.nav-burger {
    display: none;
    width: 42px;
    height: 42px;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    cursor: pointer;
    color: var(--text);
}
.nav-burger:hover { background: var(--surface-hover); }

/* La hamburguesa del PANEL se ve en los dos anchos: en escritorio pliega la barra
   lateral, en movil abre el cajon. La del sitio (sin modificador) sigue siendo
   solo movil, porque en escritorio esta la barra de navegacion. */
.nav-burger--panel { display: inline-flex; }

/* Un icono dentro de un boton redondo del header —el «Ir al sitio», que reusa la
   forma de `.theme-toggle`—. El emoji del tema se mide por font-size; un SVG
   necesita medida propia. */
.hdr-ico { width: 18px; height: 18px; }

/* Las tres barras que se cruzan en una ✕ al abrir. La del medio es el elemento;
   las de arriba y abajo son sus pseudo-elementos. */
.nav-burger__box { position: relative; width: 18px; height: 14px; }
.nav-burger__line,
.nav-burger__line::before,
.nav-burger__line::after {
    position: absolute;
    left: 0;
    width: 18px;
    height: 2px;
    border-radius: 2px;
    background: var(--text);
    transition: transform var(--speed) var(--ease), opacity var(--speed) var(--ease);
}
.nav-burger__line { top: 6px; }
.nav-burger__line::before { content: ''; top: -6px; }
.nav-burger__line::after  { content: ''; top: 6px; }
.nav-burger.is-open .nav-burger__line { background: transparent; }
.nav-burger.is-open .nav-burger__line::before { transform: translateY(6px) rotate(45deg); }
.nav-burger.is-open .nav-burger__line::after  { transform: translateY(-6px) rotate(-45deg); }

/* El panel. Fuera de la vista en escritorio pase lo que pase. */
.site-menu { display: none; }

@media (max-width: 860px) {
    .nav--desktop { display: none; }
    .hdr-only     { display: none; }
    .nav-burger   { display: inline-flex; }

    .site-menu {
        display: block;
        position: fixed;
        inset: 62px 0 auto 0;
        z-index: 49;
        max-height: calc(100dvh - 62px);
        overflow-y: auto;
        padding: var(--space-3) var(--space-4) var(--space-6);
        background: var(--bg-elevated);
        border-bottom: 1px solid var(--border);
        box-shadow: 0 24px 44px -24px rgba(0, 0, 0, 0.55);
        visibility: hidden;
        opacity: 0;
        transform: translateY(-10px);
        transition: transform 0.22s var(--ease), opacity 0.22s var(--ease), visibility 0s 0.22s;
    }
    .site-menu.is-open {
        visibility: visible;
        opacity: 1;
        transform: translateY(0);
        transition: transform 0.22s var(--ease), opacity 0.22s var(--ease), visibility 0s;
    }

    .site-menu__nav { display: flex; flex-direction: column; gap: 2px; }
    .site-menu__nav a {
        padding: var(--space-3) var(--space-2);
        border-radius: var(--radius);
        color: var(--text);
        font-size: var(--step-0);
        font-weight: 600;
    }
    .site-menu__nav a[aria-current="page"] { background: var(--surface-2); }
    .site-menu__nav a:hover { background: var(--surface-hover); }

    .site-menu__foot {
        display: flex;
        flex-direction: column;
        gap: var(--space-2);
        margin-top: var(--space-3);
        padding-top: var(--space-4);
        border-top: 1px solid var(--border);
    }
    /* El cambio de idioma, a lo ancho y con más aire para el dedo. */
    .site-menu__foot .lang-switch { align-self: stretch; justify-content: center; }
    .site-menu__foot .lang-switch__item { flex: 1; text-align: center; padding: var(--space-2); }

    /* Con el menú abierto, el fondo no se arrastra. */
    body.menu-abierto { overflow: hidden; }
}

/* Un botón a todo lo ancho — para el menú de móvil y cualquier columna. */
.btn--block { display: flex; width: 100%; justify-content: center; }

/* --------------------------------------------------------------- 5. Cards */

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--space-5);
}

.card--pad-sm { padding: var(--space-4); }
.card--flush { padding: 0; overflow: hidden; }

/* Una tarjeta que pide ATENCIÓN sin ser un error: barra ámbar a la izquierda y
   el borde teñido. Para lo importante-pero-no-urgente —completar el perfil—, como
   los avisos de «completá tu perfil» de LinkedIn: se ve, invita, no alarma. Ámbar
   y no rojo a propósito: nada está roto, falta terminar algo que conviene. */
.card--attention {
    border-color: color-mix(in srgb, var(--warn) 40%, var(--border));
    box-shadow: inset 3px 0 0 var(--warn);
}

/* La barra de GUARDAR de un formulario largo: pegada al fondo del viewport
   mientras se scrollea, para que «guardar» no se pierda arriba una vez que se baja
   a llenar los campos. El patrón de los ajustes de Shopify o GitHub. El fondo
   translúcido con blur deja ver que hay contenido debajo. */
.form-sticky {
    position: sticky;
    bottom: 0;
    z-index: 5;
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    margin-top: var(--space-5);
    padding: var(--space-3) 0 calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
    background: color-mix(in srgb, var(--bg) 86%, transparent);
    backdrop-filter: blur(10px);
    border-top: 1px solid var(--border);
}

/* En móvil los dos botones se reparten el ancho: más fáciles de alcanzar con el
   pulgar y sin dejar «Publicar» diminuto en una esquina. */
@media (max-width: 640px) {
    .form-sticky .btn { flex: 1 1 0; }
}

/* ── Mensaje (outreach y studio) ────────────────────────────────────────────
   Cómo muestran un mensaje las apps de mensajería: el OTRO —destinatario en
   outreach, remitente en studio— con su avatar arriba (no perdido en una línea
   gris), la OFERTA destacada en su propio bloque —el dato que decide si el
   creador contesta— y el texto aparte. Reusa .prose, .pill y .divider; sólo
   agrega la estructura. Las dos vistas gemelas la comparten. */
.msg__head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;              /* el precio baja debajo si no cabe (móvil) */
    row-gap: var(--space-3);
}

.msg__avatar {
    width: 48px;
    height: 48px;
    flex: 0 0 auto;
    border-radius: var(--radius-full);
    object-fit: cover;
    border: 1px solid var(--border);
    background: var(--surface-2);
    display: grid;
    place-items: center;
    font-weight: 600;
    color: var(--text-muted);
}

.msg__ident { min-width: 0; }

.msg__to-label {
    font-size: var(--step--1);
    color: var(--text-faint);
}

.msg__to-name {
    font-weight: 600;
    color: var(--text);
}

.msg__meta {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-top: 2px;
    font-size: var(--step--1);
    color: var(--text-faint);
}

/* La oferta: el precio limpio a la DERECHA —rótulo chico en mayúsculas sobre el
   monto grande—, sin caja ni fondo. Antes era una barra verde a todo lo ancho
   con el rótulo a un extremo y el número al otro, con un hueco en medio que se
   veía poco profesional; el color sobraba, el número solo ya destaca. */
.msg__offer {
    display: flex;
    flex-direction: column;
    align-items: flex-end;          /* rótulo y monto alineados a la derecha */
    gap: 2px;
    width: fit-content;
    max-width: 100%;
    margin-left: auto;              /* el precio entero, a la derecha */
    text-align: right;
}

.msg__offer-label {
    font-size: var(--step--1);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}

.msg__offer-amount {
    font-size: var(--step-2);
    font-weight: 700;
    line-height: 1.1;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

.card--link {
    display: block;
    transition: transform var(--speed) var(--ease), border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}

.card--link:hover {
    transform: translateY(-3px);
    border-color: var(--border-strong);
    box-shadow: var(--shadow);
}

/* Creator card: avatar, name, tagline, platform badges. */
.creator-card { position: relative; overflow: hidden; }

.creator-card__cover {
    height: 76px;
    background: var(--gradient-soft);
    border-bottom: 1px solid var(--border);
}

/* El banner real del canal, encima del degradado. La capa conserva su fondo
   para que al quitar la imagen (onerror) la tarjeta no quede con un hueco. */
.creator-card__cover img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.creator-card__body { padding: 0 var(--space-4) var(--space-4); }

.creator-card__avatar {
    width: 56px;
    height: 56px;
    border-radius: var(--radius-full);
    margin-top: -28px;
    border: 3px solid var(--surface);
    background: var(--gradient);
    display: grid;
    place-items: center;
    font-weight: 700;
    font-size: 1.05rem;
    color: #fff;
    object-fit: cover;
    position: relative;
}

.creator-card__name {
    font-weight: 650;
    font-size: 1.02rem;
    margin-top: var(--space-3);
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.creator-card__tagline {
    color: var(--text-muted);
    font-size: var(--step--1);
    margin-top: var(--space-1);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.creator-card__meta {
    display: flex;
    gap: var(--space-3);
    margin-top: var(--space-3);
    color: var(--text-faint);
    font-size: var(--step--1);
}

/* La tarjeta con videos (la cola de una búsqueda): el contenedor es un
   <article>, la ficha su propio enlace, y abajo una cuadrícula 2×2 de las
   últimas publicaciones del creador. */
.creator-card--vid { display: flex; flex-direction: column; }
.creator-card__link { display: block; text-decoration: none; color: inherit; }
.creator-card__videos {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-2);
    padding: 0 var(--space-4) var(--space-3);
}
.cc-vid { display: block; text-decoration: none; color: var(--text-muted); }
.cc-vid:hover { color: var(--text); }
.cc-vid__thumb {
    display: grid;
    place-items: center;
    aspect-ratio: 16 / 9;
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: var(--surface-2);
    color: var(--text-faint);
}
.cc-vid__thumb img { width: 100%; height: 100%; object-fit: cover; }
.cc-vid__t {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: var(--step--1);
    line-height: 1.3;
    margin-top: var(--space-1);
}
.cc-mas {
    display: block;
    padding: var(--space-1) var(--space-4) var(--space-4);
    color: var(--text);
    font-weight: 600;
    text-decoration: none;
}
.cc-mas:hover { text-decoration: underline; }

/* Post card. */
.post-card__thumb {
    aspect-ratio: 16 / 9;
    background: var(--gradient-soft);
    display: grid;
    place-items: center;
    font-size: 2rem;
    color: var(--text-faint);
    position: relative;
}

.post-card__duration {
    position: absolute;
    right: var(--space-2);
    bottom: var(--space-2);
    background: rgb(0 0 0 / 72%);
    color: #fff;
    font-size: 0.72rem;
    font-weight: 600;
    padding: 2px 6px;
    border-radius: var(--radius-sm);
    font-variant-numeric: tabular-nums;
}

.post-card__body { padding: var(--space-4); }

.post-card__title {
    font-weight: 620;
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.post-card__meta {
    color: var(--text-faint);
    font-size: var(--step--1);
    margin-top: var(--space-2);
    display: flex;
    gap: var(--space-2);
    align-items: center;
}

/* ------------------------------------------------------------- 6. Buttons */

.btn {
    --btn-bg: var(--surface-2);
    --btn-fg: var(--text);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: 0.6rem 1.1rem;
    border-radius: var(--radius-full);
    /* Borde FUERTE, no el tenue: el relleno secundario (`--surface-2`) apenas se
       despega de la tarjeta, así que lo que hace que se lea como botón —y no como
       texto— es el borde y la sombra. Es el mismo truco del botón secundario de
       GitHub (relleno casi blanco, borde gris visible) y del «tonal» de Material. */
    border: 1px solid var(--border-strong);
    background: var(--btn-bg);
    color: var(--btn-fg);
    font-size: var(--step--1);
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    box-shadow: var(--shadow-sm);
    transition: transform var(--speed) var(--ease), background var(--speed) var(--ease), box-shadow var(--speed) var(--ease), border-color var(--speed) var(--ease), opacity var(--speed) var(--ease);
}

/* Realimentación sutil: sube 1px y aclara el fondo. NADA de sombra grande —una
   sombra de modal en un botón pequeño se ve exagerada, y en móvil el `:hover` se
   queda pegado tras el tap («se ve raro»). El relieve base ya lo da `--shadow-sm`. */
.btn:hover { background: var(--surface-hover); transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn--primary {
    background: var(--gradient);
    color: #fff;
    border-color: transparent;
    box-shadow: var(--glow);
}

.btn--primary:hover { background: var(--gradient); filter: brightness(1.08); box-shadow: var(--glow); }

/* Fantasma = tier terciario, de verdad mínimo: sin relleno, borde tenue y SIN
   sombra, para que no compita con el secundario sólido. Casi no se usa desde que
   los CTA del panel pasaron a `.btn` sólido; queda para barras y listas. */
.btn--ghost { background: transparent; border-color: var(--border); box-shadow: none; }
.btn--ghost:hover { background: var(--surface-2); box-shadow: none; }

/* Peligro = destructivo, entonado: texto y borde rojos sin relleno ni sombra. */
.btn--danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 45%, transparent); background: transparent; box-shadow: none; }
.btn--danger:hover { background: color-mix(in srgb, var(--bad) 12%, transparent); box-shadow: none; }

.btn--sm { padding: 0.35rem 0.75rem; font-size: 0.78rem; }
.btn--lg { padding: 0.85rem 1.6rem; font-size: var(--step-0); }
.btn--block { width: 100%; }

.btn[disabled], .btn[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
}

/* ---------------------------------------------------------- 7. Pills, tags */

.pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.6rem;
    border-radius: var(--radius-full);
    font-size: 0.74rem;
    font-weight: 600;
    background: var(--surface-2);
    color: var(--text-muted);
    border: 1px solid var(--border);
    white-space: nowrap;
}

.pill--ok    { color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 35%, transparent);   background: color-mix(in srgb, var(--ok) 10%, transparent); }
.pill--warn  { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 35%, transparent); background: color-mix(in srgb, var(--warn) 10%, transparent); }
.pill--bad   { color: var(--bad);  border-color: color-mix(in srgb, var(--bad) 35%, transparent);  background: color-mix(in srgb, var(--bad) 10%, transparent); }
.pill--brand { background: var(--gradient); color: #fff; border-color: transparent; }

/* El tono neutro, y NO era opcional: `status_tone()` devuelve «muted» por
   defecto —«leído», «respondido», «archivado» y media docena más caen ahí— y
   esta clase no existía. El navegador no avisa de una clase que no existe: la
   píldora salía sin su modificador y nadie lo asociaba con un typo. Se veía en
   /outreach y en /admin a la vez. */
.pill--muted { color: var(--text-faint); border-color: var(--border); background: transparent; }

.tag-row { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.platform-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.3rem 0.7rem;
    border-radius: var(--radius-full);
    background: var(--surface-2);
    border: 1px solid var(--border);
    font-size: 0.76rem;
    font-weight: 600;
    transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease);
}

.platform-badge:hover { border-color: var(--brand-1); transform: translateY(-1px); }
.platform-badge__count { color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* La fila entera: nombre a la izquierda, seguidores a la derecha. Estaba escrito
   como `style="width:100%; justify-content:space-between"` dentro de la ficha, y
   la vista previa del estudio tenía que copiarlo para verse igual — que es la
   forma más segura de que un día dejen de verse igual. */
.platform-badge--row { width: 100%; justify-content: space-between; }

.platform-badge__name { display: inline-flex; align-items: center; gap: 0.4rem; }
.platform-badge__ico { flex: 0 0 auto; display: grid; place-items: center; color: var(--text-muted); }

/* ------------------------------------------- 8b. Enlaces del estudio

   La lista de enlaces y la vista previa de la ficha, una al lado de la otra.
   La previa se queda estrecha a propósito: en la ficha vive en la barra
   derecha, y a todo lo ancho mentiría sobre la forma que va a tener. */

.linkfx {
    display: grid;
    /* 260px y no 300: a 300 la tabla se quedaba en ~615px, justo por debajo de
       lo que necesita con sus dos botones, y «Quitar» quedaba cortado dentro
       del scroll de `.table-wrap` — o sea invisible, porque nadie arrastra una
       tabla de cinco filas para descubrir que hay una columna más. */
    grid-template-columns: minmax(0, 1fr) 260px;
    gap: var(--space-4);
    align-items: start;
}

.linkfx__vista { position: sticky; top: 80px; }

.linkfx__titulo { font-size: var(--step-0); margin-bottom: 0.2rem; }
.linkfx__subtitulo { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-faint); margin-bottom: 0.5rem; }

/* Las pestañas de plataforma de la ficha usan el mismo icono que el bloque
   «Dónde verlo» de al lado. Con el glifo de texto en una y el SVG en el otro,
   la misma plataforma se dibujaba de dos formas en la misma pantalla. */
.plat-tab__ico { flex: 0 0 auto; display: grid; place-items: center; }

.linkfx__plataforma { display: inline-flex; align-items: center; gap: 0.4rem; }
.linkfx__ico { flex: 0 0 auto; display: grid; place-items: center; color: var(--text-muted); }

/* La URL sin monoespaciada: en una tabla, la mono pesa más que el nombre de la
   plataforma de al lado y el ojo va a lo que menos importa. */
.linkfx__url { color: var(--text-muted); font-size: var(--step--1); }
.linkfx__url:hover { color: var(--brand-1); }

/* Un enlace que excede el tope del plan: sigue en la tabla para poder quitarlo o
   reordenarlo, pero atenuado, porque en la ficha pública no se ve. La opacidad
   aplica igual a la fila y a la tarjeta en que se convierte bajo 700px. */
.linkfx tr.is-oculto { opacity: 0.5; }

@media (max-width: 900px) {
    /* En una columna la previa va PRIMERO: en el teléfono lo que se quiere ver
       antes es el resultado, y la tabla de gestión pide scroll de todas formas. */
    .linkfx { grid-template-columns: minmax(0, 1fr); }
    .linkfx__vista { position: static; order: -1; }
}

/* El formulario de agregar: dos filas de dos, y la URL con doble ancho.
   `.field-grid` es `auto-fit`, así que con cuatro campos metía tres arriba y
   dejaba el cuarto solo con dos tercios de fila vacíos al lado. */
.linkform {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0 var(--space-4);
}

.linkform__ancho { grid-column: span 2; }

@media (max-width: 40rem) {
    .linkform { grid-template-columns: minmax(0, 1fr); }
    .linkform__ancho { grid-column: auto; }
}

/* --------------------------------------------------------------- 8. Forms */

.field { display: block; margin-bottom: var(--space-4); }

.field > label,
.label {
    display: block;
    font-size: var(--step--1);
    font-weight: 600;
    margin-bottom: var(--space-2);
    color: var(--text);
}

.field__help {
    font-size: 0.78rem;
    color: var(--text-faint);
    margin-top: var(--space-2);
}

input[type="text"], input[type="email"], input[type="password"], input[type="url"],
input[type="number"], input[type="date"], input[type="search"], textarea, select {
    width: 100%;
    padding: 0.65rem 0.85rem;
    background: var(--bg-elevated);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--text);
    transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}

input:focus, textarea:focus, select:focus {
    outline: none;
    border-color: var(--brand-1);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand-1) 22%, transparent);
}

textarea { resize: vertical; min-height: 7rem; line-height: 1.55; }

input::placeholder, textarea::placeholder { color: var(--text-faint); }

.check {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}

.check:hover { border-color: var(--border-strong); background: var(--surface-2); }
.check input { width: auto; margin-top: 0.2rem; accent-color: var(--brand-1); flex-shrink: 0; }
.check__text { font-size: var(--step--1); }
.check__text strong { display: block; font-weight: 620; }
.check__text span { color: var(--text-muted); }

.field-grid {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
}

/* --------------------------------------------------------- 9. Flash, alerts */

.alert {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface-2);
    font-size: var(--step--1);
    display: flex;
    gap: var(--space-3);
    align-items: flex-start;
}

/* El icono del aviso.

   `flex: 0 0 auto` para que un texto largo no lo aplaste, y `margin-top` para
   que quede ópticamente centrado con la PRIMERA línea del texto: alineado al
   borde de la caja se ve caído cuando el aviso tiene tres renglones. */
.alert__ico {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    margin-top: 0.1em;
    color: currentColor;
}

/* Medida por defecto de cualquier icono del set: crece con el texto que
   acompaña, así que un aviso, un botón y una entrada de menú lo reciben del
   tamaño correcto sin que cada uno tenga que acordarse de medirlo. Un
   contenedor con medida propia lo pisa con su propia regla. */
.ico__svg { display: block; width: 1.15em; height: 1.15em; }

.alert--ok   { border-color: color-mix(in srgb, var(--ok) 40%, transparent);   background: color-mix(in srgb, var(--ok) 10%, transparent); }
.alert--warn { border-color: color-mix(in srgb, var(--warn) 40%, transparent); background: color-mix(in srgb, var(--warn) 10%, transparent); }
.alert--bad  { border-color: color-mix(in srgb, var(--bad) 40%, transparent);  background: color-mix(in srgb, var(--bad) 10%, transparent); }

.alert ul { margin: 0; padding-left: 1.1rem; }

/* El aspa vive pegada al borde derecho pase lo que pase: `margin-left:auto`
   empuja, y el `flex:0 0 auto` evita que un texto largo la aplaste. */
.alert__close {
    margin-left: auto;
    flex: 0 0 auto;
    align-self: flex-start;
    padding: 0 var(--space-1);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: inherit;
    font-size: var(--step-1);
    line-height: 1;
    opacity: .5;
    cursor: pointer;
}

.alert__close:hover,
.alert__close:focus-visible { opacity: 1; }

/* Solo se anima lo que se cierra solo: un `.alert` fijo dentro de un formulario
   no lleva `data-flash` y nunca entra acá. */
.alert[data-flash] { transition: opacity .25s ease, transform .25s ease; }

.alert--gone {
    opacity: 0;
    transform: translateY(-4px);
}

@media (prefers-reduced-motion: reduce) {
    .alert[data-flash] { transition: none; }
}

/* ------------------------------------------- 9b. Avisos flotantes (toasts)

   LOS FLASHES SON EVENTOS; LOS `.alert` DE LA PAGINA SON ESTADO.

   Esta es la unica distincion que importa acá y por eso NO se unifican del
   todo. «Guardado.» es algo que acaba de pasar: aparece, se lee y se va. «Tu
   perfil es un borrador» es como está el mundo ahora mismo: si desapareciera
   sola, la pantalla mentiría en cuanto se fuera. Comparten el aspecto —icono,
   tono, radio, tipografía— y se diferencian en dónde viven.

   Antes el flash era `sticky` dentro del contenido: empujaba la página hacia
   abajo al aparecer, así que todo saltaba justo cuando alguien iba a leer el
   resultado de lo que acababa de hacer. Flotando no mueve nada. */

.flash-stack {
    position: fixed;
    /* ABAJO a la derecha, y no arriba.
       Arriba a la derecha es justo donde esta app pone sus botones de acción
       —`.section-head` los alinea ahí en casi todas las pantallas—, así que el
       aviso de «Guardado» se acostaba encima de «Guardar». Se detectó al
       intentar guardar dos veces seguidas: el botón se veía, respondía al
       puntero, y el clic se lo comía el aviso. Abajo no hay nada que tapar. */
    bottom: var(--space-4);
    right: var(--space-4);
    z-index: 60;
    display: grid;
    gap: var(--space-2);
    /* Ancho de lectura, no de pantalla: un aviso de cuatro palabras a todo lo
       ancho del monitor obliga a barrer la vista para leer «Guardado.» */
    width: min(26rem, calc(100vw - var(--space-4) * 2));

    /* LA CAJA NO ATRAPA CLICS; LOS AVISOS SÍ.
       Al flotar, este contenedor se pone ENCIMA del contenido: su parte vacía
       —la columna entera bajo el último aviso— se tragaba los clics de lo que
       hubiera debajo. Se detectó intentando pulsar «Guardar» con un aviso en
       pantalla: el botón se veía, respondía al puntero, y no pasaba nada.
       `auto` en cada aviso devuelve el clic donde sí hace falta: el aspa. */
    pointer-events: none;
}

.flash-stack > * { pointer-events: auto; }

/* Cada aviso entra deslizando desde su borde. `translateX` y no `opacity` sola:
   un aviso que solo aparece no dice de dónde vino, y el ojo tarda más en
   encontrarlo. */
.flash-stack .alert {
    background: var(--surface);
    box-shadow: 0 12px 34px rgb(0 0 0 / 0.16);
    animation: flash-entra var(--speed) var(--ease);
}

@keyframes flash-entra {
    from { opacity: 0; transform: translateY(0.75rem); }
    to   { opacity: 1; transform: none; }
}

/* Los tonos vuelven a pintar el fondo: sobre `--surface` opaco, el
   `color-mix` translúcido de `.alert--ok` no se veía. */
.flash-stack .alert--ok   { background: color-mix(in srgb, var(--ok) 12%, var(--surface)); }
.flash-stack .alert--warn { background: color-mix(in srgb, var(--warn) 12%, var(--surface)); }
.flash-stack .alert--bad  { background: color-mix(in srgb, var(--bad) 12%, var(--surface)); }

/* Al irse. La clase la pone el script antes de quitar el nodo. */
.flash-stack .alert--gone {
    opacity: 0;
    transform: translateY(0.75rem);
    transition: opacity var(--speed) var(--ease), transform var(--speed) var(--ease);
}

@media (max-width: 40rem) {
    /* En el teléfono baja del borde superior y ocupa el ancho: a la derecha y
       estrecho quedaría tapado por el pulgar y cortado. */
    .flash-stack {
        left: var(--space-3);
        right: var(--space-3);
        width: auto;
        bottom: var(--space-3);
    }
}

/* Quien pidió menos movimiento no recibe deslizamientos. El aviso aparece y
   desaparece igual, que es lo que tiene que hacer. */
@media (prefers-reduced-motion: reduce) {
    .flash-stack .alert { animation: none; }
    .flash-stack .alert--gone { transition: none; }
}

/* ------------------------------------------- 9c. Diálogo de confirmación

   Reemplaza al `confirm()` del navegador, que llegaba con el nombre del host
   arriba —«qcrew.test:8080 dice»—, con la tipografía del sistema y con los dos
   botones idénticos, así que borrar se veía igual que guardar. */

.confirmar {
    /* `margin: auto` a mano: el reset `* { margin: 0 }` le borra al <dialog>
       modal el centrado que el navegador le da por defecto, y sin esto el
       cuadro queda pegado a la esquina de arriba en vez de en el medio. */
    margin: auto;
    width: min(30rem, calc(100vw - var(--space-4) * 2));
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 24px 60px rgb(0 0 0 / 0.28);
}

.confirmar::backdrop { background: rgb(0 0 0 / 0.55); }

.confirmar__caja { padding: var(--space-5); }

.confirmar__texto { margin: 0 0 var(--space-5); font-size: var(--step-0); line-height: 1.5; }

/* Los botones a la derecha y en ese orden: cancelar primero, confirmar al
   final, que es donde el pulgar y el cursor terminan. */
.confirmar__botones {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* En el teléfono, los dos botones a todo el ancho, mitad y mitad: blancos de
   toque grandes y parejos, en vez de dos pastillas chicas apretadas a la
   derecha. */
@media (max-width: 30rem) {
    .confirmar__botones .btn { flex: 1 1 0; }
}

/* --------------------------------------------------------------- 10. Hero */

.hero {
    padding-block: var(--space-8) var(--space-7);
    text-align: center;
}

.hero h1 {
    font-size: var(--step-4);
    letter-spacing: -0.04em;
    max-width: 16ch;
    margin-inline: auto;
}

.hero h1 em {
    font-style: normal;
    background: var(--gradient);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.hero p {
    color: var(--text-muted);
    font-size: var(--step-1);
    max-width: 52ch;
    margin: var(--space-4) auto 0;
}

.hero__actions {
    display: flex;
    gap: var(--space-3);
    justify-content: center;
    margin-top: var(--space-6);
    flex-wrap: wrap;
}

/* ------------------------------------------------------ 11. Stats & meters */

.stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--space-4);
}

.stat__value {
    font-size: var(--step-2);
    font-weight: 750;
    letter-spacing: -0.03em;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.stat__label {
    font-size: var(--step--1);
    color: var(--text-muted);
    margin-top: var(--space-1);
}

/* Cuando la etiqueta ES un enlace —«Mensajes sin leer» lleva a la bandeja— la
   caja se estira al mínimo táctil. A la altura de su renglón medía 15px, y en
   un teléfono eso se falla con el pulgar en movimiento. */
.stat__label a {
    display: inline-flex;
    align-items: center;
    min-height: 24px;
}

.meter { margin-top: var(--space-3); }

.meter__head {
    display: flex;
    justify-content: space-between;
    font-size: var(--step--1);
    margin-bottom: var(--space-2);
}

.meter__head span:last-child { color: var(--text-muted); font-variant-numeric: tabular-nums; }

.meter__track {
    height: 7px;
    border-radius: var(--radius-full);
    background: var(--surface-2);
    overflow: hidden;
}

.meter__fill {
    height: 100%;
    border-radius: inherit;
    background: var(--gradient);
    transition: width 400ms var(--ease);
}

.meter__fill--warn { background: linear-gradient(135deg, var(--warn), var(--bad)); }
.meter__fill--full { background: var(--bad); }

/* --------------------------------------------------------- 12. Profile page */

.profile-hero {
    border-radius: var(--radius-lg);
    overflow: hidden;
    border: 1px solid var(--border);
    background: var(--surface);
}

.profile-hero__cover {
    height: clamp(120px, 22vw, 210px);
    background: var(--gradient-soft);
}

.profile-hero__body {
    position: relative;
    z-index: 2;
    padding: 0 var(--space-5) var(--space-5);
    display: flex;
    gap: var(--space-5);
    align-items: flex-end;
    flex-wrap: wrap;
}

/* Avatar + nombre EN FILA: el nombre a la derecha de la foto (ahorra alto), no
   debajo. `flex: 1` para que empuje las acciones a la derecha; el avatar mantiene
   su sobresalto sobre la portada. Solo en el encabezado del panel —la ficha
   pública arma su propio cuerpo—. */
.profile-hero__id {
    display: flex;
    align-items: flex-end;
    gap: var(--space-4);
    min-width: 0;
    flex: 1 1 auto;
}

.profile-hero__id .profile-hero__text { min-width: 0; }

/* Las acciones del encabezado de canal, en columna a la derecha: «Mis mensajes»
   arriba y «Nueva publicación» abajo. Cuando el cuerpo del hero envuelve en móvil,
   la columna baja completa. */
.profile-hero__actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-left: auto;
    align-items: stretch;
    flex: 0 0 auto;
}

/* El contador de mensajes sin leer, dentro del botón «Mis mensajes». Solo aparece
   cuando hay algo sin leer: un «0» permanente enseña al ojo a no mirar ahí. */
.count-badge {
    display: inline-grid;
    place-items: center;
    min-width: 1.3rem;
    height: 1.3rem;
    padding: 0 5px;
    margin-left: .4rem;
    border-radius: 999px;
    background: var(--brand-1);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.profile-hero__avatar {
    width: clamp(84px, 12vw, 116px);
    aspect-ratio: 1;
    border-radius: var(--radius-lg);
    margin-top: calc(-1 * clamp(42px, 6vw, 58px));
    border: 4px solid var(--surface);
    background: var(--gradient);
    display: grid;
    place-items: center;
    color: #fff;
    font-weight: 750;
    font-size: clamp(1.6rem, 3vw, 2.2rem);
    object-fit: cover;
    flex-shrink: 0;
    /* The avatar overhangs the banner by a negative margin, but the cover has
       a ::after gradient that paints over it. Both need an explicit stacking
       position or the avatar sits underneath the fade. */
    position: relative;
    z-index: 2;
}

.profile-hero__text { flex: 1 1 260px; padding-top: var(--space-4); }
.profile-hero__text h1 { font-size: var(--step-2); }
.profile-hero__text p { color: var(--text-muted); margin-top: var(--space-2); }
.profile-hero__actions { display: flex; gap: var(--space-2); padding-top: var(--space-4); flex-wrap: wrap; }

.split {
    display: grid;
    gap: var(--space-6);
    grid-template-columns: minmax(0, 1fr) 300px;
    align-items: start;
}

@media (max-width: 900px) { .split { grid-template-columns: 1fr; } }

.sticky-side { position: sticky; top: 80px; }

/* Perfil del creador -------------------------------------------------- */

/* En escritorio: Sobre y Publicaciones en la columna ancha, la barra a la
   derecha ocupando el alto de las dos. Igual que la vieja `.split`. */
.profile-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: var(--space-6);
    align-items: start;
}
.profile-body__about { grid-column: 1; }
.profile-body__posts { grid-column: 1; }
.profile-body__side  { grid-column: 2; grid-row: 1 / span 99; }

@media (max-width: 900px) {
    /* En móvil se apila y `order` manda: primero Sobre, luego la barra con
       «Dónde verlo» y «Categorías», y AL FINAL los videos. Así la información
       importante ya no queda enterrada bajo cientos de publicaciones. */
    .profile-body {
        display: flex;
        flex-direction: column;
        gap: var(--space-5);
    }
    .profile-body__about { order: 1; }
    .profile-body__side  { order: 2; }
    .profile-body__posts { order: 3; }
    /* Apilada, la barra deja de ser sticky: pegada arriba se montaría sobre lo
       que se scrollea. */
    .profile-body__side.sticky-side { position: static; }

    /* Los videos en DOS columnas: se ven varios de una vez en vez de uno por
       pantalla. El thumbnail es lo que importa; el texto se achica. */
    .grid--wide.profile-posts {
        grid-template-columns: 1fr 1fr;
        gap: var(--space-3);
    }
    .profile-posts .post-card__body { padding: var(--space-3); }
    .profile-posts .post-card__title { font-size: var(--step--1); }
    .profile-posts .post-card__meta { font-size: 0.72rem; margin-top: var(--space-1); }
}

/* Seguir / reclamar: los dos botones del perfil, con más peso y color. «Seguir»
   lleva el glow de marca; «reclamarla» deja de ser un fantasma tenue y se vuelve
   un botón sólido con borde que se enciende al pasar por encima. */
.profile-hero__actions .btn {
    padding: 0.7rem 1.5rem;
    font-size: var(--step-0);
}
.profile-hero__actions .btn--primary {
    box-shadow: 0 10px 26px -10px color-mix(in srgb, var(--brand-1) 75%, transparent);
}
.profile-hero__actions .btn--primary:hover { filter: brightness(1.08); transform: translateY(-2px); }
.profile-hero__actions .btn--ghost {
    background: var(--surface);
    border-color: var(--border-strong);
    color: var(--text);
}
.profile-hero__actions .btn--ghost:hover {
    background: var(--surface-2);
    border-color: var(--brand-1);
    transform: translateY(-2px);
}

/* Sin portada, la tarjeta arrancaba en un rectángulo plano y vacío que parecía
   a medio cargar. Un lavado del color de marca desde la esquina le da cuerpo —
   como el banner de color que generan Slack o Notion cuando no subís uno. */
.profile-hero--nocover {
    background: linear-gradient(155deg,
        color-mix(in srgb, var(--brand-1) 14%, var(--surface)),
        var(--surface) 60%);
}

@media (max-width: 900px) {
    /* Los dos botones, a todo el ancho y uno debajo del otro: es como rematan el
       perfil las apps —acción principal arriba, la otra pegada abajo—, y así
       dejan de tener anchos distintos que se veían descuadrados. */
    .profile-hero__actions {
        flex-direction: column;
        align-items: stretch;
        width: 100%;
    }
    .profile-hero__actions .btn { width: 100%; }
}

/* El bloque de control de Publicaciones —título, pestañas y buscador— va en una
   tarjeta, como el resto de las secciones del perfil. */
.posts-panel { margin-bottom: var(--space-5); }
.posts-panel .section-head { margin-bottom: var(--space-4); }
.posts-panel .plat-tabs { margin-bottom: 0; }
/* El buscador reusa `.dsc-search` (la pastilla de la portada); acá solo se ajusta
   la separación. El selector lleva las DOS clases para ganarle al margin-bottom
   que `.dsc-search` trae de fábrica —ese margen, sumado al padding de la
   tarjeta, dejaba demasiado aire debajo del buscador—. */
.dsc-search.posts-search { margin: var(--space-4) 0 0; }

/* «Cargar más»: centrado y con cuerpo, para que se lea como una acción y no como
   un botón perdido al final de la lista. */
.posts-mas { display: flex; justify-content: center; margin-top: var(--space-5); }
.posts-mas .btn { min-width: 12rem; }

/* Videos / Reels ------------------------------------------------------ */

/* El interruptor entre videos y reels: dos mitades, la activa encendida. */
.format-toggle {
    display: inline-flex;
    gap: 2px;
    margin: var(--space-4) 0 0;
    padding: 3px;
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    background: var(--surface);
}
.format-toggle__item {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.9rem;
    border-radius: var(--radius-full);
    color: var(--text-muted);
    font-size: var(--step--1);
    font-weight: 600;
}
.format-toggle__item.is-active { background: var(--gradient); color: #fff; }
.format-toggle__item:not(.is-active):hover { background: var(--surface-hover); color: var(--text); }
.format-toggle__n { opacity: 0.7; font-variant-numeric: tabular-nums; font-weight: 500; }
.format-toggle__ico { font-size: 0.7em; }

/* La cuadrícula de reels: miniaturas verticales, como el grid de Shorts. */
.reel-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 8rem), 1fr));
    gap: var(--space-2);
    margin-top: var(--space-4);
}
.reel-cell {
    position: relative;
    aspect-ratio: 9 / 16;
    border: 0;
    padding: 0;
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--gradient-soft);
    cursor: pointer;
}
.reel-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.reel-cell__ph {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-size: 1.6rem;
    color: var(--text-faint);
}
/* El triángulo de play, esquina inferior, sobre un velo para que se lea siempre. */
.reel-cell__play {
    position: absolute;
    left: var(--space-2);
    bottom: var(--space-2);
    width: 1.7rem;
    height: 1.7rem;
    display: grid;
    place-items: center;
    border-radius: var(--radius-full);
    background: rgb(0 0 0 / 55%);
    color: #fff;
    font-size: 0.7rem;
    padding-left: 2px;
}
.reel-cell:hover img { transform: scale(1.03); transition: transform var(--speed) var(--ease); }

/* El visor de reels: pantalla completa, negro, uno por pantalla ------- */
.reel-viewer {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: #000;
}
body.reel-abierto { overflow: hidden; }

.reel-viewer__track {
    height: 100dvh;
    overflow-y: scroll;
    scroll-snap-type: y mandatory;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
}
.reel-viewer__track::-webkit-scrollbar { display: none; }

.reel-slide {
    position: relative;
    height: 100dvh;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    display: grid;
    place-items: center;
    overflow: hidden;
}
/* El reproductor llena la pantalla y deja que CADA video use su proporción: un
   short vertical llena de arriba abajo (el reproductor de Shorts de YouTube se
   encarga); un video horizontal se centra a lo ancho. Antes se forzaba 9:16 y
   un video horizontal quedaba diminuto en el medio. */
.reel-slide__player,
.reel-slide__thumb {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}
.reel-slide__thumb { object-fit: cover; }
.reel-slide.is-playing .reel-slide__thumb { opacity: 0; }

/* El plan B cuando el dueño del video no permite embeberlo: el póster se queda
   (no se desvanece) y encima aparece un botón para abrirlo en YouTube, en vez
   del «error 153» atrapado. Oculto hasta que el reel se marca `is-noembed`. */
.reel-slide__fallback { display: none; }
.reel-slide.is-noembed .reel-slide__player { display: none; }
.reel-slide.is-noembed .reel-slide__fallback {
    position: absolute;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-5);
    border-radius: var(--radius-full);
    background: rgb(255 255 255 / 95%);
    color: #0a0a0a;
    font-weight: 700;
    box-shadow: 0 10px 30px -10px rgb(0 0 0 / 60%);
}
.reel-slide__fallico { font-size: 0.8em; }
.reel-slide__player iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

/* El título y el autor, abajo, sobre un degradado para que se lean sobre el video. */
.reel-slide__info {
    position: absolute;
    inset: auto 0 0 0;
    z-index: 2;
    padding: var(--space-6) var(--space-4) var(--space-4);
    background: linear-gradient(transparent, rgb(0 0 0 / 75%));
    pointer-events: none;
}
.reel-slide__title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    color: #fff;
    font-weight: 650;
    pointer-events: auto;
}
.reel-slide__meta { display: block; color: rgb(255 255 255 / 75%); font-size: var(--step--1); margin-top: var(--space-1); }

.reel-viewer__close {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    z-index: 3;
    width: 2.6rem;
    height: 2.6rem;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: var(--radius-full);
    background: rgb(0 0 0 / 45%);
    color: #fff;
    font-size: 1.1rem;
    cursor: pointer;
}
.reel-viewer__close:hover { background: rgb(0 0 0 / 65%); }

/* El botón de sonido. iOS no deja autoplay CON sonido sin un gesto: el reel
   arranca mudo (para que el autoplay pase) y este botón —un toque, que sí es
   gesto— lo enciende y lo deja encendido para los que siguen. El mismo trato
   que TikTok o Reels en el navegador del teléfono. */
.reel-viewer__sound {
    position: absolute;
    top: var(--space-3);
    right: calc(var(--space-3) + 3.2rem);
    z-index: 3;
    width: 2.6rem;
    height: 2.6rem;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: var(--radius-full);
    background: rgb(0 0 0 / 45%);
    color: #fff;
    font-size: 1.1rem;
    cursor: pointer;
}
.reel-viewer__sound:hover { background: rgb(0 0 0 / 65%); }

/* El aviso «tocá para oír», solo mientras el sonido esté apagado. Late una vez
   para que se note, y desaparece en cuanto el sonido se enciende. */
.reel-viewer.tiene-sonido .reel-viewer__hint { display: none; }
.reel-viewer__hint {
    position: absolute;
    left: 50%;
    top: var(--space-4);
    transform: translateX(-50%);
    z-index: 3;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-full);
    background: rgb(0 0 0 / 55%);
    color: #fff;
    font-size: var(--step--1);
    font-weight: 600;
    pointer-events: none;
    animation: reel-hint-late 1.6s ease-in-out 2;
}
@keyframes reel-hint-late {
    0%, 100% { opacity: 0.85; }
    50%      { opacity: 0.35; }
}

/* Auto-expand: en el teléfono, darle play a un video lo agranda a esta capa a
   pantalla completa (lo arma el JS de `layouts/main`). El video, grande y
   centrado; el negro alrededor; la ✕ para salir. */
.video-full {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: #000;
    display: grid;
    place-items: center;
}
body.video-full-abierto { overflow: hidden; }
.video-full__box {
    width: 100%;
    height: 100dvh;
}
.video-full__box iframe { width: 100%; height: 100%; border: 0; }
.video-full__close {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    z-index: 2;
    width: 2.6rem;
    height: 2.6rem;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: var(--radius-full);
    background: rgb(0 0 0 / 50%);
    color: #fff;
    font-size: 1.1rem;
    cursor: pointer;
}
.posts-search .dsc-search__clear { display: inline-block; margin: var(--space-2) 0 0 var(--space-4); color: var(--text-muted); }
.posts-search .dsc-search__clear:hover { color: var(--text); }

@media (max-width: 900px) {
    /* Las pestañas, todas del MISMO tamaño en una cuadrícula de dos columnas —el
       patrón parejo de YouTube/Instagram en móvil— en vez de flotar con anchos
       distintos en dos filas descuadradas. El contenido va centrado y, si un
       nombre no cabe con su número, se recorta en vez de desbordar. */
    .posts-panel .plat-tabs {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: var(--space-2);
    }
    .posts-panel .plat-tab {
        justify-content: center;
        min-width: 0;
        padding: 0.6rem 0.75rem;
        overflow: hidden;
    }
    /* El campo, más chico que en la portada: acá va dentro de una tarjeta. */
    .posts-search .dsc-search__input { font-size: var(--step-0); }
}

/* ---------------------------------------------------------- 13. Dash shell */

.dash {
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr);
    gap: var(--space-6);
    align-items: start;
}

/* El fondo oscuro del cajón del menú: oculto por defecto (y en escritorio); el
   @media de móvil lo enciende como overlay. Declarado acá para que quede
   listado en ESTILOS.md —el generador no indexa lo que vive dentro de @media—. */
.dash__backdrop { display: none; }

/* `minmax(0, 1fr)` y no `1fr` a secas: `1fr` es `minmax(auto, 1fr)`, o sea que
   la PISTA se ensancha hasta que quepa su contenido. Con la barra convertida en
   tira horizontal, la pista crecía a 589px y sacaba scroll a la página entera —
   y no había forma de arreglarlo desde la barra, porque el que no cedía era el
   contenedor. Es el mismo `minmax(0, …)` que ya usa la versión de escritorio. */
@media (max-width: 900px) { .dash { grid-template-columns: minmax(0, 1fr); } }

/*
 * Barra lateral plegable.
 *
 * La bandera vive en <html> y no en el `.dash` por dos razones. La primera es
 * que el script de la cabecera la aplica ANTES del primer pintado: puesta en el
 * contenedor, cada carga del panel mostraría la barra un instante antes de
 * plegarla. La segunda es que el botón que la pliega está en la cabecera, fuera
 * del `.dash`, porque tiene que seguir visible cuando la barra ya no está.
 *
 * Al plegarse la columna desaparece ENTERA, sin dejar un riel de iconos. Ya no
 * es por falta de iconos —las entradas los tienen desde que el menú se agrupó—,
 * sino porque quien pliega la barra lo hace para recuperar los 220px completos,
 * normalmente frente a una tabla ancha. Un riel devuelve la mitad y obliga a
 * adivinar por el dibujo lo que la etiqueta decía. Si algún día se quiere el
 * riel, es esta regla la que cambia, y es una decisión, no un pendiente.
 */
/* Plegar la barra es SOLO de escritorio (más espacio para una tabla ancha). En
   móvil la barra es un cajón que abre el ☰, y `data-sidebar` no la toca —si no,
   una preferencia de escritorio guardada dejaría el cajón imposible de abrir. */
@media (min-width: 901px) {
    :root[data-sidebar="off"] .dash { grid-template-columns: minmax(0, 1fr); }
    :root[data-sidebar="off"] .side-nav { display: none; }
}

.side-toggle {
    width: 34px;
    height: 34px;
    display: grid;
    place-items: center;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface-2);
    color: var(--text-muted);
    cursor: pointer;
    font-size: 0.95rem;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}

.side-toggle:hover { background: var(--surface-hover); color: var(--text); }

/* Plegada. El botón tiene que decirlo: sin la barra al lado no hay nada más en
   la pantalla que distinga "la escondí" de "esta pantalla no tiene menú". */
.side-toggle[aria-pressed="true"] {
    background: var(--gradient);
    border-color: transparent;
    color: #fff;
}

.side-nav {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    position: sticky;
    top: 80px;
}

@media (max-width: 900px) {
    /* EL MENÚ DE CUENTA ES UN CAJÓN LATERAL (drawer), COMO EN CUALQUIER PANEL.
       Antes era una tira horizontal que había que deslizar y cortaba ítems —el
       patrón que Stripe, GitHub, Notion y Shopify admin evitan—. Ahora está
       fuera de pantalla y el ☰ lo abre: vertical, con todos los ítems, y se
       cierra tocando el fondo, un ítem o Escape. */
    .side-nav {
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        z-index: 60;
        width: min(82vw, 300px);
        margin: 0;
        /* El cajon arranca en top:0, pero el header (62px, .site-header__inner)
           le queda por encima y tapaba su primer elemento —antes «Escritorio», que
           no se notaba; ahora la cabecera de identidad, que se veia cortada—. El
           padding de arriba lo despeja para que el contenido empiece bajo el
           header. */
        padding: calc(62px + var(--space-3)) var(--space-4) var(--space-4);
        overflow-y: auto;
        background: var(--surface);
        border-right: 1px solid var(--border);
        box-shadow: 0 0 40px rgb(0 0 0 / 0.35);
        transform: translateX(-100%);
        transition: transform var(--speed) var(--ease);
    }
    :root[data-drawer="open"] .side-nav { transform: translateX(0); }

    /* EL CAJÓN Y SU FONDO TIENEN QUE TAPAR EL PIE DE PÁGINA.
       El drawer es position:fixed z-index 60, pero vive dentro del `.shell` del
       panel —position:relative; z-index:1— así que queda ATRAPADO en ese
       contexto. El pie usa OTRO `.shell` (ftr__inner), también z-index:1, y como
       viene después en el DOM se pintaba ENCIMA del cajón: el «© 2026 qcrew…» se
       colaba sobre «Facturación» y «Cerrar sesión». Al abrir el cajón se eleva el
       shell del panel por encima del pie (z-index:1) pero por debajo del header
       (z-index:50), que tiene que seguir visible arriba con su ☰. */
    :root[data-drawer="open"] main .shell { z-index: 40; }

    /* El fondo oscuro tras el cajón; tocarlo cierra. Vive en el layout del panel. */
    .dash__backdrop {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 55;
        background: rgb(0 0 0 / 0.5);
        opacity: 0;
        visibility: hidden;
        transition: opacity var(--speed) var(--ease), visibility var(--speed) var(--ease);
    }
    :root[data-drawer="open"] .dash__backdrop { opacity: 1; visibility: visible; }
}

.side-nav a,
.side-nav__salir,
.side-nav__off {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    font-size: var(--step--1);
    font-weight: 550;
    white-space: nowrap;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}

.side-nav a:hover,
.side-nav__salir:hover { background: var(--surface-2); color: var(--text); }

.side-nav a[aria-current="page"] {
    background: var(--surface-2);
    color: var(--text);
    box-shadow: inset 2px 0 0 var(--brand-1);
}

/* La cabecera de identidad: avatar con iniciales, nombre y el plan que corre.
   Es un enlace a /me (el inicio) pero se lee como una ficha —no como una entrada
   mas del menu—, que es lo que la separa del «Inicio» que va justo debajo. El
   aria-current no le pone el borde de las entradas: aqui gana el fondo, sin la
   barrita, para que no compita con el resaltado del ítem activo. */
.side-nav__me {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    margin-bottom: var(--space-2);
    border-radius: var(--radius-sm);
    color: var(--text);
    text-decoration: none;
    transition: background var(--speed) var(--ease);
}

.side-nav__me:hover { background: var(--surface-2); }

.side-nav__avatar {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 2.2rem;
    height: 2.2rem;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--brand-1), var(--brand-2));
    color: #fff;
    font-size: var(--step--1);
    font-weight: 700;
}

.side-nav__me-txt { display: flex; flex-direction: column; min-width: 0; }

.side-nav__me-name {
    font-size: var(--step--1);
    font-weight: 650;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.side-nav__me-sub {
    font-size: 0.72rem;
    color: var(--text-faint);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* La ficha de identidad del MENU DE USUARIO (partials/user-menu): avatar,
   nombre y plan. NO es un enlace —dice quien sos y con que plan, como la cabecera
   de un menu de usuario cualquiera— asi que no lleva hover ni cursor de pulsar.
   La pinta la esquina en escritorio y el pie del menu movil, por eso vive fuera
   de `.side-nav`. */
.user-id {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    min-width: 0;
}

.user-id__avatar {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--brand-1), var(--brand-2));
    color: #fff;
    font-size: var(--step--1);
    font-weight: 700;
}

.user-id__txt { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }

.user-id__name {
    color: var(--text);
    font-weight: 650;
    font-size: var(--step--1);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.user-id__plan {
    color: var(--text-faint);
    font-size: 0.72rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* La ficha CON PORTADA (menu movil de un creador): un mini encabezado de canal,
   el mismo gesto que `.profile-hero` pero en chico —portada arriba, avatar que
   sobresale—. Solo en el menu movil, donde hay ancho; en la esquina de escritorio
   se usa la ficha compacta de arriba. */
.user-id--hero {
    display: block;
    padding: 0;
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--surface-2);
    border: 1px solid var(--border);
}

.user-id__cover {
    display: block;
    height: 76px;
    background: var(--gradient-soft);
}

.user-id__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Sin portada: no hay franja, y el avatar no sobresale de nada. */
.user-id--nocover .user-id__cover { display: none; }

.user-id__row {
    display: flex;
    align-items: flex-end;
    gap: var(--space-2);
    padding: 0 var(--space-3) var(--space-3);
}

.user-id--nocover .user-id__row { padding-top: var(--space-3); }

.user-id--hero .user-id__avatar {
    width: 3rem;
    height: 3rem;
    margin-top: -1.5rem;
    border: 3px solid var(--surface-2);
    object-fit: cover;
    font-size: var(--step-0);
}

.user-id--nocover .user-id__avatar { margin-top: 0; }

/* La ficha de identidad del CAJON es un enlace al inicio, pero se lee como una
   ficha —no como una entrada del menu—. Reusa `.user-id`/`.user-id--hero`, asi
   que el cajon y el menu de usuario muestran la misma pieza. */
.user-id-link {
    display: block;
    text-decoration: none;
    margin-bottom: var(--space-2);
}

/* Cerrar sesion: un enlace sutil, apartado por una linea de los accesos. No es
   un boton mas —no compite con «Mi estudio»—, como en cualquier menu de usuario. */
.signout {
    margin-top: var(--space-2);
    padding-top: var(--space-3);
    border-top: 1px solid var(--border);
}

.signout__btn {
    width: 100%;
    padding: var(--space-2);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--text-faint);
    font: inherit;
    font-size: var(--step--1);
    font-weight: 550;
    cursor: pointer;
    transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}

.signout__btn:hover { color: var(--text); background: var(--surface-2); }

/* En la esquina de escritorio va en linea, sin la franja ni el ancho completo. */
.signout--inline { margin: 0; padding: 0; border: 0; display: inline; }
.signout--inline .signout__btn { width: auto; padding: var(--space-1) var(--space-2); }

/* Cerrar sesión se dibuja como una entrada más porque es la última del menú y
   tiene que leerse dentro de él, no como un botón pegado abajo. Sigue siendo un
   <button> dentro de un <form> POST con su csrf, que es lo que importa: por GET
   lo dispara cualquier <img src> ajeno.

   El <form> va en `contents` para que no meta una caja entre la columna y el
   botón — con ella, esta entrada no se alinearía con las de arriba. */
.side-nav__form { display: contents; }

.side-nav__salir {
    width: 100%;
    border: 0;
    background: none;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
}

/* El icono de cada entrada.

   `flex: 0 0 auto` no es decorativo: sin él, «Datos del perfil» se come el
   ancho y deja su propio icono aplastado en un óvalo. */
.side-nav__ico {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 18px;
    height: 18px;
}

.side-nav__ico svg { width: 100%; height: 100%; }

/* La etiqueta se queda con el espacio sobrante, y por eso el contador termina
   pegado al borde derecho sin necesidad de un margen que lo empuje. */
.side-nav__txt { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; }

/* La entrada activa tiñe también su icono. Solo con el color del texto no se
   distingue a la distancia a la que de verdad se escanea un menú. */
.side-nav a[aria-current="page"] .side-nav__ico { color: var(--brand-1); }

/* Lo que el nivel actual no incluye: se ve, no se entra, y dice qué lo abre. */
.side-nav__off { opacity: 0.55; cursor: not-allowed; }
.side-nav__off em { font-style: normal; opacity: 0.8; }

/* El encabezado de un grupo es un <button> y no un rótulo, porque pliega lo que
   lleva debajo. Se dibuja igual de discreto que el rótulo al que reemplaza —no
   puede competir con las entradas— pero cambia de color bajo el puntero, que es
   lo único que delata que se puede pulsar. */
.side-nav__grupo {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    width: 100%;
    margin-top: var(--space-3);
    padding: var(--space-1) var(--space-3);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    font-family: inherit;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--text-faint);
    cursor: pointer;
    transition: color var(--speed) var(--ease);
}

.side-nav__grupo:hover { color: var(--text-muted); }

.side-nav__chev {
    flex: 0 0 auto;
    width: 14px;
    height: 14px;
    transition: transform var(--speed) var(--ease);
}

/* Plegado, la punta mira hacia abajo: con las entradas escondidas es la única
   señal que queda de que ahí debajo hay algo. */
.side-nav__grupo[aria-expanded="false"] .side-nav__chev { transform: rotate(180deg); }

.side-nav__items {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

/* Un `display` declarado le gana al `display:none` que el navegador aplica a
   [hidden], así que el grupo plegado hay que apagarlo a mano: sin esta línea el
   botón cambiaría de flecha y no plegaría nada. */
.side-nav__items[hidden] { display: none; }

.side-nav__label {
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--text-faint);
    padding: var(--space-3) var(--space-3) var(--space-1);
    font-weight: 700;
}

/* Sub-entradas: las pantallas que cuelgan de una sección.

   La sangría sola no alcanza para leerlas como hijas —en una columna estrecha
   se lee como un margen cualquiera—, así que va con una guía vertical, que es
   como lo resuelven los paneles que anidan (Stripe, Linear). Es un borde y no
   un ícono para que no compita con el resaltado de la entrada activa. */
.side-nav__sub {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: var(--space-1) 0 var(--space-2) calc(var(--space-3) + 3px);
    padding-left: var(--space-3);
    border-left: 1px solid var(--border);
}

.side-nav__sub a {
    font-size: 0.78rem;
    font-weight: 500;
    padding: 5px var(--space-2);
}

/* El resaltado de una hija es más liviano que el de su madre: si fueran
   iguales, dos entradas del menú se verían igual de "actuales". */
.side-nav__sub a[aria-current="page"] {
    box-shadow: none;
    color: var(--brand-1);
    background: transparent;
    font-weight: 650;
}

/* El contador de lo que está esperando. Solo se dibuja cuando hay algo. */
.side-nav__badge {
    flex: 0 0 auto;
    min-width: 1.35rem;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--brand-1);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 700;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* En móvil el menú DEJÓ de ser una tira horizontal: ahora es un cajón lateral
   vertical (ver el bloque de arriba, `.side-nav` a max-width 900px). En un cajón
   vertical los encabezados de sección SÍ significan algo y el plegado tiene su
   botón para reabrir, así que la barra se ve igual que en escritorio —con sus
   grupos «Tu perfil / Mensajes / Cuenta» y la guía vertical de las hijas—. Antes
   aquí un `display:none` sobre `.side-nav__grupo` y un `contents` sobre los ítems
   escondían toda esa organización, heredados de la tira que ya no existe: en el
   teléfono se leían once entradas planas sin una sola división. */

/* ------------------------------------------- 13c. Check de verificado

   La marca que va pegada al nombre. Se mide en `em` y no en píxeles: acompaña
   a un <h1> en la ficha y a un nombre de tarjeta que es la mitad de grande, y
   en las dos tiene que verse del tamaño de la letra que lleva al lado. Con una
   medida fija haría de globo en la tarjeta y de mota en la ficha. */

.verified {
    display: inline-grid;
    place-items: center;
    flex: 0 0 auto;
    width: 1em;
    height: 1em;
    /* Baja un pelo respecto a la línea base: centrada de verdad se ve alta,
       porque las mayúsculas de al lado no llegan al techo de la caja. */
    vertical-align: -0.08em;
}

.verified svg { width: 100%; height: 100%; display: block; }

/* El disco lleva el color de marca y NO un azul fijo: es la misma marca del
   sitio, y en el tema oscuro un azul de Instagram se apaga contra el fondo. */
.verified__disc { fill: var(--brand-1); }

/* El visto va en blanco puro y no en `--surface`: tiene que contrastar contra
   el disco, que es del mismo color en los dos temas. Atado al fondo del tema,
   en claro sería blanco sobre morado —bien— y en oscuro casi negro sobre
   morado, que no se ve. */
.verified__tick {
    fill: none;
    stroke: #fff;
    stroke-width: 2.4;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* La lista de lo que falta para el check. Es una lista de verdad —<ul>— y no
   una pila de <div>: quien la escuche con un lector de pantalla oye «lista de
   3 elementos» antes del primero, que es la mitad de la información. */
.checklist {
    list-style: none;
    margin: var(--space-4) 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.checklist__item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface-2);
    font-size: var(--step--1);
}

.checklist__ico { flex: 0 0 auto; display: grid; place-items: center; color: var(--text-faint); }

/* El texto se come el sobrante y empuja el botón al borde derecho, igual que
   en las entradas del menú lateral. */
.checklist__text { flex: 1 1 auto; }

/* Una fila de «páginas más vistas» o «países» que ADEMÁS filtra la tabla.
   Se comporta como enlace pero no se pinta como uno: en una lista de diez, diez
   textos en color de enlace compiten con el dato —la ruta y su número— que es
   justo lo que se viene a leer. El subrayado y el color aparecen al pasar. */
.visitors__filtro {
    color: inherit;
    border-radius: var(--radius-sm);
    padding: 2px var(--space-2);
    margin: -2px calc(var(--space-2) * -1);
    transition: background var(--speed) var(--ease);
}

.visitors__filtro:hover { background: var(--surface-2); }
.visitors__filtro:hover .mono, .visitors__filtro:hover .small { color: var(--brand-1); }

/* El aviso de un cobro que falló.

   Tres renglones con tres pesos distintos: qué pasó en negrita, qué hacer en
   texto normal, y el mensaje de la pasarela chico y apagado. Antes era una sola
   línea con el texto crudo de ONVO — escrito para quien integra, no para quien
   paga, y a veces en inglés. */
.pay-error__que-hacer { display: block; margin-top: 0.2rem; }

.pay-error__detalle {
    display: block;
    margin-top: 0.45rem;
    font-size: 0.78rem;
    opacity: 0.75;
}

/* El overlay de "Procesando" que tapa el formulario de pago tras confirmar.
   `position: fixed` sobre todo, para que el botón de ONVO (en su iframe) quede
   debajo y no se pueda apretar de nuevo. */
.pay-processing {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-5);
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    backdrop-filter: blur(2px);
}
.pay-processing__box {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    text-align: center;
    color: var(--text);
}

/* Anillo giratorio. Reutilizable en cualquier "cargando". */
.spinner {
    width: 2.4rem;
    height: 2.4rem;
    border-radius: 50%;
    border: 3px solid var(--border-strong);
    border-top-color: var(--brand-2);
    animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .spinner { animation-duration: 2s; }
}

/* Tarjeta guardada, TODAS con la misma forma: marca + estado arriba,
   vencimiento a la derecha, acciones abajo a lo ancho. Antes datos y botones
   iban en una fila y en móvil no cabían. */
.method-card { display: grid; gap: var(--space-3); }
.method-card__top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
}
.method-card__id { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; min-width: 0; }
.method-card__exp { white-space: nowrap; }
/* Los botones ocupan el ancho por igual: en la predeterminada queda «Quitar»
   solo a lo ancho; en las demás, «Usar por defecto» y «Quitar» mitad y mitad. */
.method-card__actions { display: flex; gap: var(--space-2); }
.method-card__actions form { flex: 1 1 0; min-width: 0; }
.method-card__actions .btn { width: 100%; }

/* El campo de una imagen: lo que hay ahora, y con qué cambiarlo.

   La miniatura va AL LADO y no encima del selector: encima, el campo empuja la
   vista previa fuera de la vista justo cuando se quiere comparar la que está
   con la que se va a poner. */
.imgcampo { display: flex; align-items: flex-start; gap: var(--space-3); }

.imgcampo__vista {
    flex: 0 0 auto;
    width: 72px;
    height: 72px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    object-fit: cover;
    background: var(--surface-2);
}

/* Sin foto: un hueco con su icono. Un cuadro vacío se lee como una imagen que
   no cargó, y manda a recargar la página en vez de a subir una. */
.imgcampo__vista--vacia { display: grid; place-items: center; color: var(--text-faint); }
.imgcampo__ico { display: grid; place-items: center; font-size: 1.5rem; }

.imgcampo__campos { flex: 1 1 auto; min-width: 0; }
.imgcampo__campos input[type="file"] { width: 100%; font-size: var(--step--1); }

/* -------------------------------------------------------------- 14. Tables */

/* Un enlace dentro de una celda es un objetivo táctil, y a la altura de su
   renglón se queda en 15px: por debajo del mínimo de la WCAG 2.2 se falla el
   toque con el pulgar en movimiento. Crece la caja, no el texto. */
td a {
    display: inline-flex;
    align-items: center;
    min-height: 24px;
    /* También a lo ancho: un creador que se llama «MK» dejaba un objetivo de
       19px, y el mínimo de la WCAG 2.2 es de caja, no solo de alto. El texto
       sigue alineado a la izquierda. */
    min-width: 24px;
}

.table-wrap {
    overflow-x: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
}

table { width: 100%; border-collapse: collapse; font-size: var(--step--1); }

/* El relleno lateral se recortó de 0.9rem a 0.7rem. En una tabla de ocho
   columnas ese cuarto de rem por lado sumaba 51px de aire — suficiente para
   que la tabla pidiera scroll horizontal en una pantalla de portátil. */
th, td { padding: 0.7rem; text-align: left; border-bottom: 1px solid var(--border); vertical-align: middle; }

th {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-faint);
    font-weight: 700;
    white-space: nowrap;
}

tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--surface-2); }

td.num { font-variant-numeric: tabular-nums; text-align: right; }

/* La columna de acciones. Siempre la última, siempre del ancho del botón: sin
   esto cada tabla le daba a sus controles el espacio que sobrara y la columna
   con el dato importante quedaba estrujada contra el borde. */
td.actions, th.actions { width: 1%; white-space: nowrap; text-align: right; }

/* Marca la tabla que en móvil se vuelve tarjetas (ver el @media de abajo), y sus
   dos variantes de rejilla. En escritorio no cambian nada —siguen siendo una
   `.table-wrap` normal—; las reglas existen a nivel superior para que queden
   LISTADAS en ESTILOS.md, porque el generador solo indexa selectores en la
   columna 0, no los que viven dentro de @media. */
.table--cards { display: block; }
.table--cards--msg { display: block; }
.table--cards--links { display: block; }
.table--cards--pay { display: block; }
.table--cards--posts { display: block; }
.table--cards--postlist { display: block; }

/* ── Las tablas se vuelven TARJETAS en móvil ─────────────────────────────────
   Una tabla de varias columnas no cabe en un teléfono, y pedir scroll horizontal
   es justo lo que las apps grandes (Gmail, Linear, Stripe) evitan: por debajo de
   cierto ancho, cada fila se apila como una tarjeta. El `<table>` y las filas NO
   cambian —el JS de filtrar/ordenar sigue operando sobre ellas—; solo cambia
   cómo se pintan, y cada celda se ubica por su `data-col`.

   La BASE (`.table--cards`) hace la transformación genérica; cada tabla trae su
   rejilla en una variante —`--msg` la bandeja, `--links` los enlaces—, porque
   sus columnas son distintas. Las acciones (50/50) son comunes a las dos. */
@media (max-width: 700px) {
    .table--cards { border: 0; background: transparent; overflow: visible; }
    .table--cards thead { display: none; }
    .table--cards table,
    .table--cards tbody,
    .table--cards tr,
    .table--cards td { display: block; }

    .table--cards tbody tr {
        display: grid;
        grid-template-columns: 1fr auto;
        gap: var(--space-2) var(--space-3);
        align-items: center;
        padding: var(--space-4);
        margin-bottom: var(--space-3);
        border: 1px solid var(--border);
        border-radius: var(--radius);
        background: var(--surface);
    }

    /* Sin resaltado al pasar por encima: en táctil no hay «encima». */
    .table--cards tbody tr:hover { background: var(--surface); }
    /* `min-width: 0` es la pieza clave: un item de grid trae `min-width: auto`,
       que le impide encoger bajo el ancho de su contenido y hace que la tarjeta
       se desborde a la derecha. Con esto la celda encoge y el texto largo
       envuelve en vez de empujar. */
    .table--cards tbody tr td { padding: 0; border: 0; white-space: normal; min-width: 0; }

    /* Rejilla de la BANDEJA: asunto arriba, remitente y estado, monto y fecha. */
    .table--cards--msg tbody tr {
        grid-template-areas:
            "subject subject"
            "who     status"
            "budget  time"
            "actions actions";
    }
    .table--cards--msg td[data-col="subject"]  { grid-area: subject; }
    .table--cards--msg td[data-col="subject"] a { font-size: var(--step-0); font-weight: 600; color: var(--text); overflow-wrap: anywhere; }
    .table--cards--msg td[data-col="who"]      { grid-area: who; color: var(--text-muted); }
    .table--cards--msg td[data-col="status"]   { grid-area: status; justify-self: end; }
    .table--cards--msg td[data-col="budget"]   { grid-area: budget; text-align: left; font-weight: 600; }
    .table--cards--msg td[data-col="time"]     { grid-area: time; justify-self: end; }

    /* Rejilla de los ENLACES: plataforma y seguidores, la URL abajo. */
    .table--cards--links tbody tr {
        grid-template-areas:
            "platform followers"
            "url      url"
            "actions  actions";
    }
    .table--cards--links td[data-col="platform"]  { grid-area: platform; font-weight: 600; }
    .table--cards--links td[data-col="url"]       { grid-area: url; overflow-wrap: anywhere; }
    .table--cards--links td[data-col="followers"] { grid-area: followers; justify-self: end; text-align: left; color: var(--text-muted); }

    /* Rejilla del HISTORIAL DE PAGOS: el plan y el monto arriba, fecha y estado
       debajo, y el comprobante al pie. */
    .table--cards--pay tbody tr {
        grid-template-areas:
            "plan   amount"
            "date   status"
            "receipt receipt";
    }
    .table--cards--pay td[data-col="plan"]    { grid-area: plan; font-weight: 600; }
    .table--cards--pay td[data-col="amount"]  { grid-area: amount; justify-self: end; text-align: right; font-weight: 600; }
    .table--cards--pay td[data-col="date"]    { grid-area: date; }
    .table--cards--pay td[data-col="status"]  { grid-area: status; justify-self: end; }
    .table--cards--pay td[data-col="receipt"] { grid-area: receipt; }

    /* Tus publicaciones: titulo (largo, con hashtags) y vistas arriba; tipo y
       estado debajo; en la lista completa, la fecha; y las acciones a lo ancho.
       Dos variantes: el resumen del inicio (`--posts`, sin fecha) y la lista
       entera (`--postlist`, con fecha y borrar). */
    .table--cards--posts tbody tr {
        grid-template-areas:
            "title  views"
            "type   status"
            "actions actions";
    }
    .table--cards--postlist tbody tr {
        grid-template-areas:
            "title   views"
            "type    status"
            "updated updated"
            "actions actions";
    }
    .table--cards--posts td[data-col="title"],
    .table--cards--postlist td[data-col="title"]    { grid-area: title; font-weight: 600; color: var(--text); overflow-wrap: anywhere; }
    .table--cards--postlist td[data-col="title"] a  { color: var(--text); }
    .table--cards--posts td[data-col="views"],
    .table--cards--postlist td[data-col="views"]    { grid-area: views; justify-self: end; text-align: right; color: var(--text-muted); font-variant-numeric: tabular-nums; }
    .table--cards--posts td[data-col="type"],
    .table--cards--postlist td[data-col="type"]     { grid-area: type; color: var(--text-muted); }
    .table--cards--posts td[data-col="status"],
    .table--cards--postlist td[data-col="status"]   { grid-area: status; justify-self: end; }
    .table--cards--postlist td[data-col="updated"]  { grid-area: updated; color: var(--text-faint); font-size: var(--step--1); }

    /* Acciones comunes: mitad y mitad, a todo el ancho de la tarjeta. Cada
       acción —enlace o <form>— se reparte por igual y el botón llena su mitad;
       con una sola acción, ocupa todo. */
    .table--cards td[data-col="actions"]  { grid-area: actions; margin-top: var(--space-2); }
    .table--cards td[data-col="actions"] .row { gap: var(--space-2); }
    .table--cards td[data-col="actions"] .row > * { flex: 1 1 0; min-width: 0; margin: 0; }
    .table--cards td[data-col="actions"] .row .btn { width: 100%; }
}

/* Menú «⋯» de una fila -------------------------------------------------

   Reemplaza las filas de botones sueltos: una tabla de treinta filas servía
   treinta formularios y noventa controles, y el ojo no encontraba el dato.
   Ver partials/row-actions.php. */

.row-actions { position: relative; display: inline-block; }

.row-actions__trigger {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    color: var(--text-muted);
    font-size: 1.05rem;
    line-height: 1;
    user-select: none;
}

/* El triángulo que Safari y Chrome le ponen a <summary> por su cuenta: al lado
   de los tres puntos son dos marcas para una sola cosa. */
.row-actions__trigger::-webkit-details-marker { display: none; }
.row-actions__trigger::marker { content: ''; }

.row-actions__trigger:hover,
.row-actions[open] .row-actions__trigger {
    background: var(--surface-2);
    border-color: var(--border);
    color: var(--text);
}

.row-actions__trigger:focus-visible {
    outline: 2px solid var(--brand-1);
    outline-offset: 2px;
}

.row-actions__menu {
    position: absolute;
    right: 0;
    top: calc(100% + 4px);
    z-index: 30;
    min-width: 12rem;
    display: flex;
    flex-direction: column;
    padding: var(--space-1);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: 0 10px 30px rgb(0 0 0 / 0.18);
    text-align: left;
}

.row-actions__menu form { margin: 0; display: block; }
/* El dato que una acción PIDE, dentro del menú. «Entrar como» necesita un
   motivo —el registro de haber entrado en la cuenta de otra persona— y pedirlo
   acá evita una pantalla entera para escribir un renglón. */
.row-actions__campo { display: block; padding: var(--space-1) var(--space-2) 0; }
.row-actions__campo input { width: 100%; font-size: var(--step--1); }


.row-actions__item {
    display: block;
    width: 100%;
    padding: var(--space-2) var(--space-3);
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: var(--step--1);
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}

.row-actions__item:hover { background: var(--surface-2); }

/* Destructivas: van al final y se leen rojas ANTES de pulsarlas. En un menú
   corto la distancia es lo único que separa «Ver» de «Eliminar». */
.row-actions__item.is-danger { color: var(--bad); }
.row-actions__item.is-danger:hover { background: color-mix(in oklab, var(--bad) 12%, transparent); }

/* Apagada con su motivo debajo: una acción que desaparece sin decir por qué se
   lee como un fallo del panel. */
.row-actions__item.is-off { color: var(--text-faint); cursor: not-allowed; }
.row-actions__item.is-off small { display: block; font-size: 0.7rem; opacity: 0.8; white-space: normal; }

/* La última fila abriría su menú fuera de la caja con scroll. */
.table-wrap:has(.row-actions[open]) { overflow: visible; }

@media (max-width: 700px) {
    .row-actions__menu { right: auto; left: 0; }
}

/* --------------------------------------------------------- 15. Category tree */

.cat-tree { display: grid; gap: var(--space-2); }

.cat-tree__node {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface);
}

.cat-tree__node--l2 { margin-left: var(--space-5); background: var(--surface-2); }
.cat-tree__node--l3 { margin-left: calc(var(--space-5) * 2); background: transparent; border-style: dashed; }

.cat-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0.4rem 0.85rem;
    border-radius: var(--radius-full);
    border: 1px solid var(--border);
    background: var(--surface);
    font-size: var(--step--1);
    font-weight: 560;
    transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease);
}

.cat-chip:hover { border-color: var(--brand-1); transform: translateY(-1px); }
.cat-chip[aria-current="page"] { background: var(--gradient); color: #fff; border-color: transparent; }

/* ------------------------------------------------------------- 16. Pricing */

/* La moneda actual cuando NO hay dónde elegir (una sola soportada): en vez de un
   pill chico y gris, el valor grande y legible, para que se sepa en qué se cobra. */
.currency-now { font-size: var(--step-1); font-weight: 700; color: var(--text); }

/* Control segmentado mensual/anual (estilo iOS). Ocupa todo el ancho con las
   dos opciones mitad y mitad —cómodo en el pulgar—, y la activa se levanta sobre
   una superficie clara. Las opciones son enlaces `?cobro=`: sin JavaScript
   recargan; con él, el toggle cambia el panel sin recargar. */
/* Mensual/anual: DOS BOTONES, no una barra de pestañas sutil. El patrón de
   barra (pista + tono) no se leía como cliqueable —la opción apagada parecía
   texto—. Acá las dos son pastillas con borde y sombra, iguales al sistema de
   CTA, y la activa va RELLENA con la marca: se ve que las dos se tocan y cuál
   está puesta. (Vercel/Linear/Framer hacen el toggle de periodo así.) */
.segmented {
    display: flex;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

.segmented__opt {
    flex: 1 1 0;
    min-width: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45rem;
    padding: 0.7rem 0.9rem;
    border-radius: var(--radius-full);
    border: 1px solid var(--border-strong);
    background: var(--surface-2);
    box-shadow: var(--shadow-sm);
    color: var(--text-muted);
    font-weight: 600;
    font-size: var(--step--1);
    line-height: 1.2;
    text-align: center;
    cursor: pointer;
    transition: transform var(--speed) var(--ease), background var(--speed) var(--ease), color var(--speed) var(--ease), border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}

.segmented__opt:hover { background: var(--surface-hover); color: var(--text); transform: translateY(-1px); }

.segmented__opt.is-on {
    background: var(--gradient);
    color: #fff;
    border-color: transparent;
    box-shadow: var(--glow);
}

.segmented__opt.is-on:hover { transform: translateY(-1px); filter: brightness(1.05); }

/* «2 meses gratis» sobre el degradado activo: en blanco translúcido, que el
   verde sobre morado se apagaba. Sobre la opción apagada mantiene su verde. */
.segmented__opt.is-on .pill { background: rgb(255 255 255 / 0.22); color: #fff; border-color: transparent; }

/* El canje de cupón: input + botón en una fila; el input toma el ancho. */
.coupon-box__form { display: flex; gap: var(--space-2); }
.coupon-box__input {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.5rem 0.8rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-strong);
    background: var(--surface-2);
    color: var(--text);
    font-size: var(--step--1);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.coupon-box__input::placeholder { color: var(--text-faint); letter-spacing: 0.06em; }

.price-grid {
    display: grid;
    gap: var(--space-5);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
    align-items: start;
}

.plan { position: relative; display: flex; flex-direction: column; height: 100%; }

.plan--highlight {
    border-color: var(--brand-1);
    box-shadow: var(--glow);
}

.plan__price {
    font-size: var(--step-3);
    font-weight: 780;
    letter-spacing: -0.04em;
    margin-block: var(--space-3) var(--space-1);
}

.plan__period { color: var(--text-faint); font-size: var(--step--1); font-weight: 500; }

.plan__features { list-style: none; padding: 0; margin: var(--space-5) 0; display: grid; gap: var(--space-3); flex: 1; }

/* -------- Cambiar de plan: cada opción es mejora o baja, detalle plegado --------
   La tarjeta se estira a la misma altura de la fila y el botón queda pegado
   abajo (`margin-top:auto` en el form), para que boten alineados. */
.plan-opt { display: flex; flex-direction: column; height: 100%; }

/* La MEJORA se distingue de un vistazo con un filo de marca; la BAJA queda
   neutra —es una opción válida, no se castiga con rojo—. */
.plan-opt--up { border-color: color-mix(in srgb, var(--brand-3) 45%, var(--border)); }
.plan-opt--down { opacity: 0.94; }

.plan-opt__price { display: flex; align-items: baseline; gap: 0.4rem; flex-wrap: wrap; margin-top: var(--space-3); }
.plan-opt__big { font-size: var(--step-1); font-weight: 700; color: var(--text); }

/* El total del año, el precio de referencia tachado y el ahorro, en una línea.
   Es el anclaje: el anual se lee como ganga porque tiene el «antes» al lado. */
.plan-opt__save { display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem 0.5rem; margin: 0.4rem 0 0; font-size: var(--step--1); }
.plan-opt__total { font-weight: 700; color: var(--text); }

/* Detalle PLEGADO con `<details>` nativo: primero la decisión (cuánto, sube o
   baja), las features a un clic. El `summary` es una PASTILLA con borde —antes
   era texto gris con una flechita y no se notaba que se hacía clic—. La flecha
   gira al abrir; se oculta el marcador por defecto del navegador. */
.plan-details { margin: var(--space-4) 0; }
.plan-details > summary {
    cursor: pointer;
    list-style: none;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    width: fit-content;
    padding: 0.3rem 0.8rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-full);
    background: var(--surface-2);
    color: var(--text);
    font-size: var(--step--1);
    font-weight: 600;
    transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.plan-details > summary::-webkit-details-marker { display: none; }
.plan-details > summary::after { content: "▾"; transition: transform var(--speed) var(--ease); }
.plan-details[open] > summary::after { transform: rotate(180deg); }
.plan-details > summary:hover { background: var(--surface-hover); }

.plan__features li {
    display: flex;
    gap: var(--space-3);
    font-size: var(--step--1);
    align-items: flex-start;
}

.plan__features li::before {
    content: "✓";
    color: var(--ok);
    font-weight: 700;
    flex-shrink: 0;
}

.plan__features li[data-off]::before { content: "✕"; color: var(--text-faint); }
.plan__features li[data-off] { color: var(--text-faint); }

/* --------------------------------------------------------------- 17. Misc */

.empty {
    text-align: center;
    padding: var(--space-7) var(--space-4);
    color: var(--text-muted);
    border: 1px dashed var(--border);
    border-radius: var(--radius);
}

/* `grid` para centrar: cuando el icono es un SVG del set y no un carácter, el
   dibujo es `display:block` y sin esto se pega al borde izquierdo. */
.empty__icon {
    display: grid;
    place-items: center;
    font-size: 2.4rem;
    margin-bottom: var(--space-3);
    opacity: 0.65;
}

/* ------------------------------------------- 13b. Barra de lista (buscar,
   filtrar, ordenar)

   Una tabla larga sin estas tres cosas obliga a leerla entera para encontrar
   una fila. Va como bloque propio —y no dentro de la pantalla que lo estrenó—
   porque la siguiente lista larga lo necesita igual. */

/* Dos desplegables arriba y el buscador debajo. En móvil una tabla de chips se
   iba a varias líneas; dos <select> ocupan una línea, son el control que todos
   reconocen, y el buscador —lo más ancho— va debajo, a todo el ancho. */
.listbar {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

/* Los dos desplegables (estado y orden), SIEMPRE mitad y mitad —también en el
   teléfono— para que juntos midan lo mismo que el buscador de abajo. `flex-basis:
   0` reparte el ancho por igual sin un mínimo que los mande a dos líneas, y
   `min-width: 0` deja que el <select> encoja en vez de desbordar. */
.listbar__row { display: flex; gap: var(--space-2); }
.listbar__row .listbar__sort { flex: 1 1 0; min-width: 0; }
/* `min-width: 0` TAMBIÉN en el <select>: un control de formulario nativo trae un
   ancho mínimo propio que el min-width del contenedor no toca, y sin esto los
   dos desplegables no encogen y se desbordan a la derecha en el teléfono. */
.listbar__row .listbar__sort select { width: 100%; min-width: 0; }

.listbar__search { width: 100%; min-width: 0; }

.listbar__filters { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.listbar__chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    /* Altura mínima táctil: estos son los controles que más se pulsan de la
       pantalla, y a la altura de su texto se quedaban en 20px. */
    min-height: 24px;
    padding: 0.25rem 0.7rem;
    border-radius: var(--radius-full);
    border: 1px solid var(--border);
    background: var(--surface-2);
    color: var(--text-muted);
    font-family: inherit;
    font-size: var(--step--1);
    font-weight: 550;
    white-space: nowrap;
    cursor: pointer;
    transition: border-color var(--speed) var(--ease), color var(--speed) var(--ease);
}

.listbar__chip:hover { border-color: var(--border-strong); color: var(--text); }

.listbar__chip--on { border-color: var(--brand-1); color: var(--brand-1); }

/* El contador de cada estado. Más chico y en tabulares para que los números no
   bailen de ancho al filtrar. */
.listbar__n {
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;
    opacity: 0.75;
}

/* Sin `margin-inline-start: auto`: empujado al extremo derecho, en cuanto la
   fila se llenaba de chips el orden caía a una línea propia y quedaba solo,
   pegado al borde, con medio ancho de hueco al lado. Fluyendo detrás de los
   filtros se lee como la tercera herramienta de la misma barra, y cuando no
   cabe empieza donde empiezan las demás. */
.listbar__sort select { min-width: 12rem; }

.listbar__count {
    margin-bottom: var(--space-3);
    font-size: var(--step--1);
    color: var(--text-faint);
}

.listbar__empty {
    margin-top: var(--space-4);
    text-align: center;
    color: var(--text-faint);
}

/* El número del título —«8 mensajes recibidos»— resaltado en el color de marca,
   para que la cifra salte sin necesidad de un pill aparte al costado. */
.count-hl { color: var(--brand-1); font-weight: 800; }

/* «Cargar más»: centrado, y a todo el ancho en móvil para un buen blanco de
   toque. Reemplaza a la paginación —una bandeja no se pagina, se revela—. */
.load-more { margin-top: var(--space-4); display: flex; justify-content: center; }
.load-more .btn { min-width: 12rem; }

@media (max-width: 40rem) {
    .load-more .btn { width: 100%; }
}

.muted { color: var(--text-muted); }
.faint { color: var(--text-faint); }
.small { font-size: var(--step--1); }
.mono { font-family: var(--font-mono); font-size: 0.82rem; }
.nowrap { white-space: nowrap; }

/* Un texto que no puede ensanchar a su contenedor: recorta con puntos
   suspensivos y el entero se deja en el `title`. Existe por las tablas del
   panel, donde una ruta larga o el nombre de un proveedor empujaban la tabla
   hasta pedir scroll horizontal — y donde envolverlo en dos líneas era peor,
   porque estiraba TODAS las filas para que una celda cupiera.
   El ancho se ajusta por caso con `style="--trunc: 30ch"`. */
.trunc {
    display: block;
    max-width: var(--trunc, 22ch);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.center { text-align: center; }

.divider { height: 1px; background: var(--border); border: 0; margin-block: var(--space-5); }

.prose { max-width: 68ch; line-height: 1.75; }
.prose p + p { margin-top: var(--space-4); }

.breadcrumb {
    display: flex;
    gap: var(--space-2);
    align-items: center;
    font-size: var(--step--1);
    color: var(--text-faint);
    flex-wrap: wrap;
    margin-bottom: var(--space-4);
}

.breadcrumb a:hover { color: var(--text); }
.breadcrumb span::after { content: "/"; margin-left: var(--space-2); opacity: 0.5; }

.pager { display: flex; gap: var(--space-2); justify-content: center; margin-top: var(--space-6); flex-wrap: wrap; }
.pager a, .pager strong {
    padding: 0.4rem 0.8rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    font-size: var(--step--1);
}
.pager strong { background: var(--gradient); color: #fff; border-color: transparent; }
.pager a:hover { background: var(--surface-2); }

.filter-bar {
    display: flex;
    gap: var(--space-3);
    align-items: center;
    flex-wrap: wrap;
    padding: var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin-bottom: var(--space-5);
}

.filter-bar input[type="search"], .filter-bar select { width: auto; min-width: 180px; flex: 1 1 180px; }

/* -------------------------------------------------------------- 18. Footer */

/*
 * El pie del sitio.
 *
 * Sustituye a `.site-footer` y `.landing-footer`, que eran la misma tira
 * horizontal escrita dos veces: copyright, unos enlaces sueltos, redes y
 * legales, todos del mismo tamaño y sin agrupar. Eso es una barra de utilidad;
 * un pie tiene que decir de qué va el sitio y dejar saltar al grupo de enlaces
 * que a uno le sirve sin leerlos todos.
 *
 * Dos franjas: arriba la marca y las columnas con título, abajo una barra fina
 * con el año, quién lo hizo y las redes, separada por una línea. Es la forma
 * que usan los directorios y los SaaS, y la razón es que agrupar por tema es lo
 * único que hace escaneable una lista de nueve enlaces.
 */
.ftr {
    border-top: 1px solid var(--border);
    margin-top: auto;
    color: var(--text-faint);
    font-size: var(--step--1);
}

.ftr__inner { padding-block: var(--space-6); }

.ftr a { color: var(--text-muted); }
.ftr a:hover { color: var(--text); }

/* La marca a la izquierda con su lema, las columnas empujadas a la derecha.
   `auto` en la primera y no un ancho fijo: el lema tiene 46ch de tope, así que
   la marca ocupa lo que necesita y el resto es para los enlaces. */
.ftr__top {
    display: grid;
    grid-template-columns: minmax(14rem, 22rem) 1fr;
    gap: var(--space-6);
    align-items: start;
    padding-bottom: var(--space-6);
}

/* `min-width: 0` en la columna de la marca: sin él, el lema impone su ancho
   mínimo a la celda de la parrilla y empuja las columnas de enlaces fuera. */
.ftr__brand { min-width: 0; }

.ftr__tagline {
    margin: var(--space-2) 0 0;
    color: var(--text-faint);
    max-width: 30ch;
    line-height: 1.5;
}

/* `auto-fit` y no tres columnas fijas: al agregar una cuarta sección se acomoda
   sola, y al estrecharse baja a dos y luego a una sin un solo media query. */
.ftr__cols {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: var(--space-5) var(--space-4);
}

.ftr__col { display: flex; flex-direction: column; gap: var(--space-2); }

/* El título del grupo es lo que convierte nueve enlaces en tres listas de tres.
   Va en mayúsculas y más pequeño que los enlaces: tiene que ordenarlos, no
   competir con ellos. */
.ftr__colTitle {
    font-size: 0.68rem;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--text-faint);
    font-weight: 700;
    margin: 0 0 var(--space-1);
}

/* La columna legal reusa el partial de enlaces legales, que se dibuja en fila.
   Acá tiene que apilarse como las demás. */
.ftr__col--legal .legal-links {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    margin: 0;
}

.ftr__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-4);
    padding-top: var(--space-5);
    border-top: 1px solid var(--border);
}

/* En modo compacto la barra es todo el pie y no lleva nada encima que separar. */
.ftr--compact .ftr__bar { padding-top: 0; border-top: 0; }

.ftr__legalLine { display: flex; align-items: center; gap: var(--space-2); margin: 0; flex-wrap: wrap; }
.ftr__dot { color: var(--border); }

.ftr__end { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }

.built-by { margin: 0; color: var(--text-faint); }
.built-by a { color: var(--text-muted); font-weight: 600; }
.built-by a:hover { color: var(--brand-1); }

@media (max-width: 700px) {
    /* La marca deja de compartir fila con los enlaces: a 320px, dos columnas
       dejan al lema en una tira de seis caracteres de ancho. */
    .ftr__top { grid-template-columns: 1fr; gap: var(--space-5); }
    .ftr__bar { justify-content: flex-start; }
}

/* Redes del pie. El mismo bloque sirve en el pie del sitio y en el de la
   landing porque los dos incluyen el MISMO partial: si estas reglas vivieran en
   la seccion de la landing, el pie del sitio saldria con los iconos sin estilo
   y nadie lo notaria hasta ver la portada logueado. */

.social-links { display: flex; align-items: center; gap: var(--space-2); margin: 0; }
.social-links__label { color: var(--text-faint); font-size: var(--step--1); }

.social-links__link {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-full);
    border: 1px solid transparent;
    color: var(--text-faint);
    transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}

.social-links__link:hover { color: var(--text); background: var(--surface-2); border-color: var(--border); }

/* `display: block` en el icono: un <svg> en linea se sienta sobre la linea base
   y deja un par de pixeles muertos abajo que descentran el circulo. */
.social-links__icon { width: 18px; height: 18px; display: block; }
.social-links__glyph { font-size: 1rem; line-height: 1; }

/* ------------------------------------------------------- 19. Theme toggle */

.theme-toggle {
    width: 34px;
    height: 34px;
    display: grid;
    place-items: center;
    border-radius: var(--radius-full);
    border: 1px solid var(--border);
    background: var(--surface-2);
    cursor: pointer;
    font-size: 0.95rem;
    transition: background var(--speed) var(--ease);
}

.theme-toggle:hover { background: var(--surface-hover); }

/* --------------------------------------------------------- 20. Entrance */

@keyframes rise {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}

.rise { animation: rise 420ms var(--ease) both; }
.rise:nth-child(2) { animation-delay: 40ms; }
.rise:nth-child(3) { animation-delay: 80ms; }
.rise:nth-child(4) { animation-delay: 120ms; }

/* ==========================================================================
   21. Landing page
   Only the home and the closing CTA use these. Kept at the end so the
   component layer above stays the reusable part of the system.
   ========================================================================== */

.eyebrow {
    display: inline-block;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--brand-2);
    opacity: 0.95;
    margin-bottom: var(--space-3);
}

/* --- Aurora: three slow blurred blobs behind the hero. Purely decorative,
       and `contain: paint` stops the blur from forcing repaints elsewhere. --- */
.aurora {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    contain: paint;
}

.aurora__blob {
    position: absolute;
    border-radius: 50%;
    filter: blur(90px);
    opacity: 0.22;
    will-change: transform;
}

.aurora__blob--1 {
    width: 46vw; height: 46vw; min-width: 320px; min-height: 320px;
    background: var(--brand-1);
    top: -14%; left: -8%;
    animation: drift-a 22s ease-in-out infinite alternate;
}

.aurora__blob--2 {
    width: 38vw; height: 38vw; min-width: 260px; min-height: 260px;
    background: var(--brand-2);
    top: 6%; right: -6%;
    animation: drift-b 26s ease-in-out infinite alternate;
}

.aurora__blob--3 {
    width: 30vw; height: 30vw; min-width: 220px; min-height: 220px;
    background: var(--brand-3);
    bottom: -18%; left: 32%;
    animation: drift-c 30s ease-in-out infinite alternate;
    opacity: 0.12;
}

@keyframes drift-a { to { transform: translate3d(8vw, 5vh, 0) scale(1.15); } }
@keyframes drift-b { to { transform: translate3d(-7vw, 8vh, 0) scale(0.9); } }
@keyframes drift-c { to { transform: translate3d(5vw, -6vh, 0) scale(1.2); } }

/* Light mode needs far less of it or the text stops being readable. */
:root[data-theme="light"] .aurora__blob { opacity: 0.13; }
@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) .aurora__blob { opacity: 0.13; }
}

/* --- Hero --- */
.hero-xl {
    position: relative;
    overflow: hidden;
    background: var(--bg);
    /* BANNER A TODO EL ANCHO, no tarjeta.
       El contenido de la portada vive dentro de `.shell` (1200px centrados), y
       el muro de caras encerrado ahí se leía como una tarjeta con textura. Los
       margenes negativos lo sacan del shell hasta el borde de la ventana sin
       tocar el layout de nada de lo que viene debajo.
       OJO: `50vw` incluye la barra de scroll, asi que en Windows y Linux esto
       sobresale ~15px y la pagina acaba con scroll HORIZONTAL. Lo que lo evita
       es el `overflow-x: clip` del body — `clip` y no `hidden` porque `hidden`
       crea un contenedor de scroll y rompe el `position: sticky` del header. */
    margin-inline: calc(50% - 50vw);
    padding-inline: max(1rem, calc(50vw - 600px));
    border-bottom: 1px solid var(--border);
}

/* Pegado al header, sin la franja de fondo que dejaba en medio.
   Esa franja es el `padding-block` superior de `main` (--space-6), que existe
   para todas las demás pantallas; acá se cancela con la misma medida.

   `:first-child` NO es decorativo: si arriba hay un flash o el aviso de base de
   datos caída, el banner deja de ser el primer hijo, la regla no aplica y el
   aviso no queda tapado por el mosaico. Con un margen negativo fijo, el banner
   se subía encima del aviso justo el día que hay algo que avisar. */
main > .shell > .hero-xl:first-child {
    margin-top: calc(var(--space-6) * -1);
    border-top: 0;
}

.hero-xl__inner {
    position: relative;
    z-index: 1;
    text-align: center;
    padding: clamp(3rem, 9vw, 6.5rem) clamp(1.25rem, 5vw, 4rem);
}

.hero-xl__title {
    font-size: var(--step-4);
    letter-spacing: -0.045em;
    max-width: 15ch;
    margin-inline: auto;
    text-wrap: balance;
}

.hero-xl__title em {
    font-style: normal;
    background: var(--gradient);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.hero-xl__lede {
    color: var(--text-muted);
    font-size: var(--step-1);
    max-width: 56ch;
    margin: var(--space-5) auto 0;
}

.hero-xl__actions {
    display: flex;
    gap: var(--space-3);
    justify-content: center;
    flex-wrap: wrap;
    margin-top: var(--space-6);
}

.hero-xl__chips {
    display: flex;
    gap: var(--space-3);
    justify-content: center;
    flex-wrap: wrap;
    margin-top: var(--space-6);
}

.chip {
    padding: 0.4rem 0.9rem;
    border-radius: var(--radius-full);
    border: 1px solid var(--border);
    background: color-mix(in srgb, var(--surface) 70%, transparent);
    backdrop-filter: blur(8px);
    font-size: var(--step--1);
    color: var(--text-muted);
}

.chip strong { color: var(--text); font-variant-numeric: tabular-nums; }

/* Frosted button, for use on top of the aurora. */
.btn--glass {
    background: color-mix(in srgb, var(--surface) 55%, transparent);
    backdrop-filter: blur(10px);
    border-color: var(--border-strong);
}

.btn--glass:hover { background: color-mix(in srgb, var(--surface) 85%, transparent); }

/* --- Category marquee --- */
.marquee {
    margin-top: var(--space-6);
    overflow: hidden;
    /* Fades both ends so items enter and leave instead of being chopped. */
    mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.marquee__track {
    display: flex;
    gap: var(--space-3);
    width: max-content;
    animation: slide 38s linear infinite;
}

.marquee:hover .marquee__track { animation-play-state: paused; }

/* Translating exactly -50% works because the list is rendered twice. */
@keyframes slide { to { transform: translateX(-50%); } }

@media (prefers-reduced-motion: reduce) {
    .marquee__track { animation: none; flex-wrap: wrap; width: auto; justify-content: center; }
    .marquee { mask-image: none; }
}

/* --- Bands --- */
.band { margin-top: var(--space-8); }

.band__title { font-size: var(--step-2); letter-spacing: -0.03em; }

.band__lede {
    color: var(--text-muted);
    max-width: 58ch;
    margin-top: var(--space-3);
}

.section-head--center { justify-content: center; text-align: center; }
.section-head--center .band__lede { margin-inline: auto; }

/* --- Bento grid --- */
.bento {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}

.bento__cell {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--space-5);
    transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease);
}

.bento__cell:hover { border-color: var(--border-strong); transform: translateY(-2px); }

/* Wide cells span two tracks where there is room for two. */
@media (min-width: 900px) { .bento__cell--wide { grid-column: span 2; } }

.bento__icon { font-size: 1.6rem; display: block; margin-bottom: var(--space-3); }
.bento__cell h3 { font-size: var(--step-1); margin-bottom: var(--space-2); }
.bento__cell p { color: var(--text-muted); font-size: var(--step--1); }

/* --- Two-audience split --- */
.split-cta {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
}

.split-cta__side {
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: clamp(1.5rem, 4vw, 2.5rem);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.split-cta__side--creator { background: linear-gradient(160deg, color-mix(in srgb, var(--brand-1) 9%, var(--surface)), var(--surface)); }
.split-cta__side--brand   { background: linear-gradient(160deg, color-mix(in srgb, var(--brand-2) 9%, var(--surface)), var(--surface)); }

.split-cta__side h2 { font-size: var(--step-2); letter-spacing: -0.03em; }

.ticks { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--space-3); flex: 1; }

.ticks li {
    display: flex;
    gap: var(--space-3);
    font-size: var(--step--1);
    color: var(--text-muted);
    align-items: flex-start;
}

.ticks li::before { content: "✓"; color: var(--ok); font-weight: 700; flex-shrink: 0; }

/* --- Steps --- */
.steps {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
    counter-reset: step;
}

.steps__item {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--space-5);
    position: relative;
}

.steps__num {
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    border-radius: var(--radius-full);
    background: var(--gradient);
    color: #fff;
    font-weight: 750;
    margin-bottom: var(--space-4);
}

.steps__item h3 { font-size: var(--step-1); margin-bottom: var(--space-2); }
.steps__item p { color: var(--text-muted); font-size: var(--step--1); }

/* --- Closing CTA --- */
.cta-band {
    position: relative;
    margin-top: var(--space-8);
    border-radius: var(--radius-lg);
    overflow: hidden;
    border: 1px solid var(--border);
    background: var(--bg-elevated);
}

.cta-band__inner {
    position: relative;
    z-index: 1;
    text-align: center;
    padding: clamp(2.5rem, 7vw, 4.5rem) var(--space-5);
}

.cta-band h2 { font-size: var(--step-2); letter-spacing: -0.03em; }
.cta-band p { color: var(--text-muted); margin: var(--space-3) 0 var(--space-6); }

/* --- Portal --- */
.portal {
    display: grid;
    place-items: center;
    min-height: 68vh;
    padding-block: var(--space-6);
}

.portal__card { width: min(100%, 420px); }

.portal__mark {
    width: 56px;
    height: 56px;
    margin-inline: auto;
    display: grid;
    place-items: center;
    border-radius: var(--radius);
    background: var(--gradient);
    color: #fff;
    font-size: 1.6rem;
    box-shadow: var(--glow);
}

/* --- Language switcher ---------------------------------------------------- */

.lang-switch {
    display: inline-flex;
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    overflow: hidden;
    background: var(--surface-2);
}

.lang-switch__item {
    padding: 0.28rem 0.6rem;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    color: var(--text-faint);
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}

.lang-switch__item:hover { background: var(--surface-hover); color: var(--text); }

.lang-switch__item.is-active {
    background: var(--gradient);
    color: #fff;
}

/* --- Pagination ----------------------------------------------------------- */

.pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-top: var(--space-7);
}

.pager__pages { display: flex; gap: var(--space-1); align-items: center; }

.pager__page,
.pager__step {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    min-width: 38px;
    height: 38px;
    padding: 0 0.7rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--surface);
    font-size: var(--step--1);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
    transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease), color var(--speed) var(--ease);
}

.pager__page:hover,
.pager__step:hover {
    background: var(--surface-hover);
    border-color: var(--border-strong);
    color: var(--text);
}

.pager__page.is-current {
    background: var(--gradient);
    border-color: transparent;
    color: #fff;
}

/* Kept in the flow rather than removed, so the control does not jump width
   between the first page and the middle ones. */
.pager__step.is-disabled {
    opacity: 0.4;
    pointer-events: none;
}

.pager__gap {
    padding: 0 0.3rem;
    color: var(--text-faint);
    user-select: none;
}

/* En móvil el pager envolvía y «Siguiente» caía solo en un renglón aparte,
   descolgado y con un hueco enorme debajo. Ahora es UNA fila: Anterior y
   Siguiente fijos a los lados, los números en el medio y con scroll propio si
   son muchos (89 páginas no caben, pero tampoco tienen por qué). */
@media (max-width: 40rem) {
    .pager {
        margin-top: var(--space-5);
        flex-wrap: nowrap;
        gap: var(--space-2);
    }
    /* Anterior/Siguiente se quedan en la flecha: «Anterior» y «Siguiente» juntos
       comían el ancho y aplastaban los números hasta un scroll de 70px inservible.
       El texto no se borra —se oculta accesiblemente— así que el lector de
       pantalla lo sigue anunciando. */
    .pager__step { flex: 0 0 auto; padding: 0; min-width: 40px; }
    .pager__txt {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
    }
    .pager__pages {
        display: flex;
        flex-wrap: nowrap;
        gap: var(--space-1);
        justify-content: center;
    }
}

@media (max-width: 560px) {
    /* On a phone the step buttons are what people actually use. */
    .pager__step span:not([aria-hidden]) { display: none; }
    .pager__step { padding: 0 0.6rem; min-width: 38px; }
}

/* --- Admin category tree with per-language editing ------------------------ */

.cat-node {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    margin-bottom: var(--space-2);
}

.cat-node--l2 { margin-left: var(--space-5); background: var(--surface-2); }
.cat-node--l3 { margin-left: calc(var(--space-5) * 2); border-style: dashed; background: transparent; }

.cat-node__row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    flex-wrap: wrap;
}

/* El nombre lleva al editor y es lo único que se pulsa en la fila. La ruta se
   empuja al final para que las 268 filas alineen su columna de acciones: con la
   ruta pegada al nombre, el menú «⋯» caía a un ancho distinto en cada fila y el
   ojo tenía que buscarlo en cada una. */
.cat-node__name { font-weight: 600; color: var(--text); }
.cat-node__name:hover { color: var(--brand-1); }
.cat-node__path { margin-left: auto; }
.cat-node__go { color: var(--text-faint); white-space: nowrap; }
.cat-node__go:hover { color: var(--brand-1); }

/* Miga de pan. Existe para las pantallas a las que solo se llega desde una
   lista —editar una categoría, ver un pago—: sin ella no dicen de qué rama
   salieron ni cómo volver, y la única salida es el botón del navegador. */
.crumbs {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    color: var(--text-faint);
    margin-bottom: var(--space-3);
}

.crumbs a { color: var(--text-muted); }
.crumbs a:hover { color: var(--text); }
.crumbs [aria-current="page"] { color: var(--text); font-weight: 600; }

.cat-node__i18n { border-top: 1px solid var(--border); }

.cat-node__i18n > summary {
    padding: var(--space-2) var(--space-3);
    cursor: pointer;
    font-size: var(--step--1);
    color: var(--text-muted);
    user-select: none;
}

.cat-node__i18n > summary:hover { color: var(--text); }
.cat-node__i18n > form { padding: 0 var(--space-3) var(--space-3); }

.cat-node__langs { display: grid; gap: var(--space-2); }

.cat-node__lang {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.cat-node__lang input { flex: 1 1 150px; min-width: 110px; }

/* --- Video embeds --------------------------------------------------------- */

.video {
    position: relative;
    aspect-ratio: var(--video-ratio, 16 / 9);
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--surface-2);
    border: 1px solid var(--border);
}

/* Vertical formats would otherwise take over the page on a wide screen. */
.video:has(.video__facade[data-embed*="tiktok"]),
.video:has(.video__facade[data-embed*="instagram"]) {
    max-width: 420px;
    margin-inline: auto;
}

.video iframe,
.video__facade {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

.video__facade {
    padding: 0;
    cursor: pointer;
    background: var(--gradient-soft);
    display: grid;
    place-items: center;
}

.video__thumb {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* YouTube's hqdefault is 4:3 with black bars; a slight scale crops them. */
    transform: scale(1.02);
}

.video__thumb--blank {
    display: grid;
    place-items: center;
    font-size: clamp(2rem, 8vw, 4rem);
    color: var(--text-faint);
    background: var(--gradient-soft);
}

.video__play {
    position: relative;
    z-index: 1;
    width: 68px;
    height: 68px;
    border-radius: var(--radius-full);
    display: grid;
    place-items: center;
    font-size: 1.5rem;
    color: #fff;
    padding-left: 4px; /* optical centring of the triangle */
    background: rgb(0 0 0 / 55%);
    backdrop-filter: blur(6px);
    border: 2px solid rgb(255 255 255 / 85%);
    transition: transform var(--speed) var(--ease), background var(--speed) var(--ease);
}

.video__facade:hover .video__play {
    transform: scale(1.08);
    background: var(--brand-1);
}

.video__badge {
    position: absolute;
    left: var(--space-3);
    bottom: var(--space-3);
    z-index: 1;
    padding: 0.2rem 0.6rem;
    border-radius: var(--radius-full);
    background: rgb(0 0 0 / 65%);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.03em;
}

/* Real thumbnails on the feed cards, replacing the placeholder gradient. */
.post-card__thumb img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* --- Profile cover -------------------------------------------------------
   The creator's real channel banner, or nothing at all. There is no generated
   fallback on purpose: an invented gradient in a banner slot reads as "this is
   theirs" when it is not.
   ------------------------------------------------------------------------- */

.profile-hero__cover {
    position: relative;
    z-index: 0;
    overflow: hidden;
    /* Banners are 6:1. Capped so a wide screen does not turn the top of the
       page into a billboard, and floored so a phone still shows something. */
    aspect-ratio: 6 / 1;
    min-height: 92px;
    max-height: 260px;
    background: var(--surface-2);
}

.profile-hero__cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Fades the banner into the card so the avatar overlapping it has contrast. */
.profile-hero__cover::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(to bottom, transparent 55%, var(--surface));
}

/* No banner: the body just needs its own top padding, since it was relying on
   the cover for the avatar overhang. */
.profile-hero--nocover .profile-hero__body { padding-top: var(--space-5); }
.profile-hero--nocover .profile-hero__avatar { margin-top: 0; }
/* --- Generated profile cover ---------------------------------------------
   There is no banner in the dataset, so the cover is the creator's own avatar
   blurred behind a tint of their category colour. No extra request: the
   browser already fetched that image for the avatar itself.
   ------------------------------------------------------------------------- */

.profile-hero__cover {
    position: relative;
    z-index: 0;
    overflow: hidden;
    /* Falls back to the brand colour when the creator has no category. */
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--cover-accent, var(--brand-1)) 30%, var(--surface)),
        var(--surface));
}

.profile-hero__cover-blur {
    position: absolute;
    inset: -20%;
    width: 140%;
    height: 140%;
    object-fit: cover;
    filter: blur(44px) saturate(150%);
    opacity: 0.55;
    transform: scale(1.1);
}

/* Keeps the avatar and name readable over whatever the blur turned out to be. */
.profile-hero__cover::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
        to bottom,
        color-mix(in srgb, var(--cover-accent, var(--brand-1)) 18%, transparent),
        var(--surface) 96%);
}

/* The small strip on a creator card gets the same treatment, tint only. */
.creator-card__cover {
    background: linear-gradient(135deg,
        color-mix(in srgb, var(--card-accent, var(--brand-1)) 22%, var(--surface)),
        var(--surface));
}

/* --- Platform tabs on a creator profile ----------------------------------- */

.plat-tabs {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-bottom: var(--space-4);
}

.plat-tab {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 0.9rem;
    border-radius: var(--radius-full);
    border: 1px solid var(--border);
    background: var(--surface);
    font-size: var(--step--1);
    font-weight: 600;
    color: var(--text-muted);
    transition: border-color var(--speed) var(--ease), color var(--speed) var(--ease), background var(--speed) var(--ease);
}

.plat-tab:hover { border-color: var(--border-strong); color: var(--text); }

.plat-tab.is-active {
    background: var(--gradient);
    border-color: transparent;
    color: #fff;
}

.plat-tab__n {
    font-variant-numeric: tabular-nums;
    opacity: 0.7;
    font-weight: 500;
}

/* Una plataforma sin contenido indexado aquí: enlaza AFUERA en vez de abrir una
   pestaña propia. Antes su borde era punteado y el texto apagado, así que rompía
   el estándar de la fila y parecía deshabilitada. Ahora es el mismo pill que los
   demás; lo único que la distingue es la flecha en un chip —«esto abre en la
   plataforma»—, que es justo la diferencia que hay que contar. */
.plat-tab--out { color: var(--text-muted); }
.plat-tab--out:hover { color: var(--text); }

.plat-tab__ext {
    display: inline-grid;
    place-items: center;
    width: 1.15rem;
    height: 1.15rem;
    border-radius: var(--radius-full);
    background: color-mix(in srgb, var(--brand-1) 16%, transparent);
    color: var(--brand-1);
    font-size: 0.72rem;
    font-weight: 700;
}

/* --- Embedded platform profile widget ------------------------------------- */

.plat-embed {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: var(--space-4) 0;
}

/* The platform injects its own iframe into the blockquote; give it a floor so
   the page does not jump when the script swaps the placeholder out. */
.plat-embed .tiktok-embed {
    min-height: 640px;
    width: 100%;
    margin: 0;
}

/* ==========================================================================
   Landing de "lo que viene"  (prefijo .lp-)
   --------------------------------------------------------------------------
   Solo se pinta cuando Qcrew::$landingMode esta encendido. Vive al final del
   mismo archivo y reusa los tokens de :root — no hay una paleta aparte, para
   que un cambio de marca siga siendo un cambio en un solo lugar.
   ========================================================================== */

/* La landing anula el padding global de `main`.
   La regla `main { padding-block: var(--space-6) var(--space-8) }` es del sitio
   principal, donde cada pagina necesita aire arriba. Aca el hero llega hasta el
   borde y ese padding dejaba una franja de fondo plano sobre el degradado: se
   veia como una barra negra con un corte duro debajo del header. */
.landing-body { position: relative; }
.landing-body main { padding-block: 0; }

.skip-link {
    position: absolute;
    left: -9999px;
    top: var(--space-3);
    z-index: 100;
    padding: var(--space-3) var(--space-4);
    background: var(--brand-1);
    color: #fff;
    border-radius: var(--radius-sm);
}
.skip-link:focus { left: var(--space-4); }

.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* Franja propia, opaca, pegada al hero.
   El hueco que se veia antes NO era del header: era el `padding-block` global
   de `main` (ver .landing-body main mas abajo). Con eso anulado, la franja
   puede quedarse y el degradado arranca justo debajo, sin banda muerta.
   `sticky` para que acompane el scroll; sin borde inferior, porque el cambio
   de color ya separa y una linea extra volvia a leerse como costura. */
.landing-header {
    position: sticky;
    top: 0;
    z-index: 40;
    padding: var(--space-4) 0;
    background: var(--bg);
    border-bottom: 0;
}
.landing-header__inner { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }

/* ------------------------------------------------------------------ hero */

/* El relleno de arriba bajo de 6rem a 4.5rem cuando la insignia se mudo al
   contador: esos 96px estaban calculados para que la insignia respirara bajo la
   cabecera, y sin ella quedaban de vacio puro sobre el titular. */
.lp-hero { position: relative; overflow: hidden; padding: clamp(2.5rem, 5vw, 4.5rem) 0 clamp(2.5rem, 6vw, 5rem); }
.lp-hero__inner { position: relative; z-index: 1; text-align: center; max-width: 54rem; }

/* --- El muro de caras --------------------------------------------------------
   Lo usan los dos heroes: la landing de coming soon y la portada del sitio
   completo. La imagen la compone `php spark qcrew:collage` desde los avatares
   reales del directorio; acá solo se coloca y se apaga lo justo para que el
   titular gane. El velo va aparte, en cada hero, porque cada uno se apoya sobre
   un fondo distinto.                                                          */
.face-wall {
    position: absolute;
    /* -1px por lado: al escalar con object-fit queda una costura de medio pixel
       contra el borde de la seccion, y se ve como una linea clara. */
    inset: -1px;
    width: calc(100% + 2px);
    height: calc(100% + 2px);
    object-fit: cover;
    object-position: center;
    pointer-events: none;
    user-select: none;
    /* Casi a plena vista. La primera version iba al 30% y con una máscara que
       BORRABA el centro — justo donde uno mira — así que el muro no se veía.
       Lo que protege al texto ahora es un charco de color detrás de él (el
       ::after de cada hero), no apagar la imagen entera. */
    opacity: 0.92;
}

/* El velo va en el color del tema, así el mismo archivo sirve para los dos:
   sobre claro aclara, sobre oscuro oscurece. Y cierra contra el fondo por
   abajo, para que el mosaico no termine en un corte recto.

   Son dos reglas y no una lista de selectores porque el token NO es el mismo:
   la landing se apoya en el fondo de la página y el hero de la portada es una
   tarjeta sobre --bg-elevated. Con un solo token, el degradado del hero cierra
   contra un color que no es el suyo y deja un borde visible.                  */
.lp-hero::after,
.hero-xl::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
        /* UNA COLUMNA, NO UNA ELIPSE.
           El velo tiene que cubrir la columna de contenido ENTERA y a toda su
           altura. Con una elipse centrada arriba, todo lo que va debajo —el
           formulario, los chips, la letra chica— quedaba sobre el mosaico a
           plena vista y no se leía. Una columna protege a cualquier altura, y
           deja los tercios de los lados con las caras nítidas, que es donde de
           verdad se lucen. */
        linear-gradient(to right,
            transparent 0%,
            color-mix(in srgb, var(--bg) 55%, transparent) 17%,
            color-mix(in srgb, var(--bg) 90%, transparent) 33%,
            color-mix(in srgb, var(--bg) 90%, transparent) 67%,
            color-mix(in srgb, var(--bg) 55%, transparent) 83%,
            transparent 100%),
        /* Y el cierre contra la página: sin esto el mosaico termina en un corte
           recto que se ve como un error de recorte. */
        linear-gradient(to bottom,
            color-mix(in srgb, var(--bg) 25%, transparent) 0%,
            transparent 26%,
            transparent 60%,
            var(--bg) 100%);
}

/* EN MOVIL LA COLUMNA NO SIRVE: EL TEXTO OCUPA TODO EL ANCHO.
   El velo de arriba protege del 33% al 67% porque en escritorio el contenido
   vive en una columna centrada. A 390px el titular, la bajada y la letra chica
   del formulario llegan de borde a borde, o sea que sus extremos caen justo en
   la parte transparente y quedan sobre las caras a plena vista. Aca el velo
   pasa a ser parejo. */
@media (max-width: 48rem) {
    .lp-hero::after,
    .hero-xl::after {
        background:
            linear-gradient(to bottom,
                color-mix(in srgb, var(--bg) 78%, transparent) 0%,
                color-mix(in srgb, var(--bg) 86%, transparent) 45%,
                var(--bg) 100%);
    }

    /* Y la letra chica del formulario va en `--text-faint`, el tono mas apagado
       de la paleta: sobre una tarjeta lisa se lee, sobre el muro de caras no,
       ni con halo. Sube un escalon SOLO aca — en escritorio ya se lee bien y
       cambiarla ahi seria arreglar lo que no esta roto. */
    .lp-hero .lp-form__fine { color: var(--text-muted); }
}

/* Halo del color del fondo detrás del texto chico que cae sobre el muro. Es lo
   que permite dejar las caras a la vista sin que la bajada pierda contraste
   contra una casilla clara: subir el velo hasta que la bajada se leyera sola
   apagaba el mosaico entero, que es justo lo que había que evitar.

   NO se le pone al <em> del titular: ese lleva `color: transparent` con
   `background-clip: text`, y ahí el text-shadow se dibuja DETRÁS del recorte y
   sale como un borrón con la forma de las letras. */
/* El eyebrow va en morado de marca: un tono medio que sobre un mosaico de
   fotos se pierde con halo y todo. Se resuelve con una píldora sólida, que es
   lo que ya hace `.lp-badge` en la landing y ahí sí se lee. */
.hero-xl__inner .eyebrow {
    display: inline-block;
    padding: 0.35rem 0.85rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-full);
    background: color-mix(in srgb, var(--bg) 88%, transparent);
}

.hero-xl__lede,
.lp-hero__lede,
.lp-hero__proof,
.lp-form__fine,
.lp-form__note,
.lp-form__promise {
    text-shadow: 0 1px 10px var(--bg), 0 0 24px var(--bg);
}

.lp-badge {
    display: inline-flex; align-items: center; gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-full);
    background: var(--surface);
    font-size: var(--step--1);
    color: var(--text-muted);
}
.lp-badge__dot {
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--ok);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 22%, transparent);
    animation: lp-pulse 2.4s var(--ease) infinite;
}
.lp-badge__sep { opacity: .5; }
@keyframes lp-pulse { 50% { opacity: .45; } }

/* Ese margen existia para separar el titular de la insignia. Ahora el titular
   es el primer elemento del hero, y el margen se suma al relleno de la seccion:
   dos separaciones para una sola union. Se anula justo en ese caso. */
.lp-hero__inner > .lp-hero__title:first-child { margin-top: 0; }

.lp-hero__title {
    margin: var(--space-5) 0 0;
    font-size: var(--step-4);
    line-height: 1.05;
    letter-spacing: -0.03em;
    text-wrap: balance;
}
.lp-hero__title em {
    font-style: normal;
    background: var(--gradient);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    display: block;
}
.lp-hero__lede {
    margin: var(--space-5) auto 0;
    max-width: 42rem;
    font-size: var(--step-1);
    color: var(--text-muted);
    line-height: 1.6;
    text-wrap: pretty;
}
.lp-hero__proof { margin-top: var(--space-5); font-size: var(--step--1); color: var(--text-faint); }
.lp-hero__proof strong { color: var(--text); }

/* El buscador que se escribe solo. Ancho fijo por caracteres para que la caja
   no se ensanche y encoja con cada letra y arrastre el layout con ella. */
.lp-searchbox {
    display: flex; align-items: center; gap: var(--space-3);
    width: min(100%, 34rem);
    margin: var(--space-6) auto var(--space-5);
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-full);
    background: var(--surface);
    box-shadow: var(--shadow);
    text-align: left;
}
.lp-searchbox__icon { color: var(--text-faint); font-size: 1.2em; }
.lp-searchbox__text { color: var(--text); font-size: var(--step-0); min-height: 1.4em; }
.lp-searchbox__caret {
    width: 2px; height: 1.15em;
    background: var(--brand-2);
    animation: lp-blink 1s steps(2) infinite;
}
@keyframes lp-blink { 50% { opacity: 0; } }

/* ------------------------------------------------------------- formulario */

.lp-form { width: min(100%, 34rem); margin-inline: auto; text-align: left; }
.lp-form__row { display: flex; gap: var(--space-3); flex-wrap: wrap; }
.lp-form__input {
    flex: 1 1 15rem;
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-full);
    background: var(--bg-elevated);
    color: var(--text);
    font: inherit;
}
.lp-form__input:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; border-color: transparent; }
.lp-form__submit { flex: 0 0 auto; border-radius: var(--radius-full); padding-inline: var(--space-6); }

/* Trampa para bots: fuera de pantalla, no display:none. Algunos bots saltan lo
   que esta oculto pero si llenan lo que "existe" en el DOM. */
.lp-form__trap { position: absolute; left: -9999px; opacity: 0; pointer-events: none; }

.lp-form__who { display: flex; gap: var(--space-2); flex-wrap: wrap; border: 0; padding: 0; margin: var(--space-4) 0 0; }
.lp-chip-radio input { position: absolute; opacity: 0; width: 0; height: 0; }
.lp-chip-radio span {
    display: inline-block;
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    font-size: var(--step--1);
    color: var(--text-muted);
    cursor: pointer;
    transition: all var(--speed) var(--ease);
}
.lp-chip-radio input:checked + span { border-color: var(--brand-2); color: var(--text); background: var(--gradient-soft); }
.lp-chip-radio input:focus-visible + span { outline: 2px solid var(--brand-2); outline-offset: 2px; }

.lp-form__note { margin: var(--space-3) 0 0; font-size: var(--step--1); min-height: 1.4em; }
.lp-form__note .is-ok, .lp-form__note.is-ok { color: var(--ok); }
.lp-form__note .is-bad, .lp-form__note.is-bad { color: var(--bad); }
.lp-form__fine { margin: var(--space-2) 0 0; font-size: var(--step--1); color: var(--text-faint); }

/* ----------------------------------------------------------------- bandas */

/* RELLENO EN UNA SOLA DIRECCION, Y ESE ES TODO EL TRUCO.
   El problema no era el valor sino que se SUMABA: con 5.5rem por lado quedaban
   176px de vacio en cada union, y como todas las bandas comparten el mismo
   fondo, ese hueco no se lee como separacion sino como que la pagina se acabo.
   Midiendo los sitios de verdad (2026-09-10) se ve que Stripe alterna
   `padding-bottom: 0` justo para no duplicar; Linear y Notion usan 96-128 por
   lado pero cambian el fondo de cada banda, asi que ahi el hueco SI separa.
   Aca las bandas van sin fondo propio, asi que el relleno va solo abajo: 64px
   entre bloque y bloque, uno de los valores estandar, y no dos sumados. */
.lp-band { padding: 0 0 clamp(2.5rem, 5vw, 4rem); }
.lp-narrow { max-width: 48rem; }
.lp-head-center { text-align: center; max-width: 44rem; margin-inline: auto; }
.lp-band__title { margin: var(--space-3) 0 0; font-size: var(--step-2); letter-spacing: -0.02em; text-wrap: balance; }
.lp-band__lede { margin: var(--space-4) 0 0; color: var(--text-muted); font-size: var(--step-1); line-height: 1.6; text-wrap: pretty; }

/* Hoy vs con qcrew. Grid de dos y no float para que en movil se apilen en el
   orden correcto sin tocar el HTML. */
.lp-contrast { list-style: none; padding: 0; margin: var(--space-6) 0 0; display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.lp-contrast__item { padding: var(--space-5); border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); }
.lp-contrast__item p { margin: var(--space-3) 0 0; color: var(--text-muted); line-height: 1.6; }
.lp-contrast__label { font-size: var(--step--1); text-transform: uppercase; letter-spacing: .08em; font-weight: 600; }
.lp-contrast__item--bad { border-color: color-mix(in srgb, var(--bad) 28%, var(--border)); }
.lp-contrast__item--bad .lp-contrast__label { color: var(--bad); }
.lp-contrast__item--good { border-color: color-mix(in srgb, var(--ok) 30%, var(--border)); background: var(--gradient-soft); }
.lp-contrast__item--good .lp-contrast__label { color: var(--ok); }

/* --------------------------------------------------------- circulo virtuoso */

.lp-loop { list-style: none; padding: 0; margin: var(--space-6) 0 0; display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); counter-reset: loop; }
.lp-loop__node { position: relative; padding: var(--space-5); border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); }
.lp-loop__node h3 { margin: var(--space-3) 0 0; font-size: var(--step-1); }
.lp-loop__node p { margin: var(--space-3) 0 0; color: var(--text-muted); line-height: 1.6; }
.lp-loop__label { font-size: var(--step--1); text-transform: uppercase; letter-spacing: .08em; font-weight: 600; color: var(--brand-2); }

/* Flecha entre nodos: dibuja el "circulo" sin una imagen. Se apaga en una sola
   columna, donde apuntaria hacia el lado equivocado. */
.lp-loop__node::after {
    content: "→";
    position: absolute;
    right: calc(var(--space-4) * -1);
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-faint);
    font-size: 1.1rem;
}
.lp-loop__node:last-child::after { content: "↺"; right: auto; left: 50%; top: auto; bottom: calc(var(--space-5) * -1); transform: translateX(-50%); }
@media (width < 52rem) { .lp-loop__node::after { display: none; } }

.lp-loop__closing {
    margin: var(--space-6) auto 0;
    max-width: 44rem;
    text-align: center;
    font-size: var(--step-1);
    line-height: 1.6;
    color: var(--text);
    text-wrap: pretty;
}

/* ------------------------------------------- el contador de lanzamiento */

/* Arriba Y abajo. Iba con `0` abajo, y como `.lp-form` no trae margen propio
   —se apoyaba en el que dejaba `.lp-searchbox`, que ahora ya no es su vecino—
   el rotulo «Lista de lanzamiento» quedaba pegado a las cajas del reloj. */
.lp-countdown { margin: var(--space-6) auto; }

/* La insignia encabeza el reloj. `.lp-badge` es `inline-flex`, asi que sin este
   bloque queda pegada a la izquierda en vez de centrada sobre las cajas. */
.lp-countdown__badge { margin: 0 0 var(--space-4); }

.lp-countdown__row {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    gap: clamp(0.25rem, 1.5vw, 0.75rem);
}

.lp-countdown__unit {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    min-width: clamp(3.75rem, 9vw, 5.5rem);
    padding: clamp(0.5rem, 1.5vw, 0.9rem) clamp(0.35rem, 1vw, 0.75rem);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    /* Semitransparente y no `--surface` opaco: el contador va sobre el muro de
       caras, y una caja solida ahi tapa justo lo que se quiere enseniar. */
    background: color-mix(in srgb, var(--bg-elevated) 72%, transparent);
    backdrop-filter: blur(6px);
}

.lp-countdown__num {
    font-size: clamp(1.75rem, 5vw, 2.75rem);
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.02em;
    /* Cifras de ancho fijo. Sin esto el «1» es mas angosto que el «8» y la caja
       se encoge y se estira cada segundo: el contador entero tiembla. */
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

.lp-countdown__label {
    font-size: 0.68rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.lp-countdown__sep {
    align-self: center;
    /* Alineado con los numeros y no con la caja: los rotulos de abajo bajan el
       centro de la unidad, y los dos puntos quedaban flotando. */
    margin-top: clamp(0.4rem, 1.2vw, 0.9rem);
    font-size: clamp(1.25rem, 3vw, 1.9rem);
    font-weight: 700;
    color: var(--text-faint);
    opacity: 0.55;
}

/* ------------------------------------- capturas del producto en la landing */

/* Capturas del sitio de verdad, no una maqueta: las genera
   `python3 tools/previews.py`. El patron —captura enmarcada, con titulo encima,
   apiladas y con aire— es el que usan Stripe, Linear y Vercel en sus paginas de
   producto; no hay razon para inventar otro.                                  */
.lp-shots {
    display: grid;
    /* 2rem entre capturas y no 3: con el titulo de cada una encima, 3rem partia
       la seccion en tres bloques sueltos en vez de leerse como una secuencia. */
    gap: var(--space-6);
    margin-top: var(--space-5);
}

.lp-mock-note {
    margin: var(--space-4) auto 0;
    max-width: 40rem;
    text-align: center;
    font-size: var(--step--1);
    color: var(--text-muted);
}

.lp-shot { margin: 0; }

.lp-shot__caption {
    display: block;
    margin-bottom: var(--space-3);
    text-align: center;
    font-size: var(--step--1);
    color: var(--text-muted);
}

/* `--shot-dark` / `--shot-light` y el `aspect-ratio` los pone la vista, sacados
   del archivo real. Como es un FONDO, el navegador solo descarga la que el tema
   activo referencia — con dos <img> se bajaria tambien la del tema contrario. */
.lp-shot__img {
    background-image: var(--shot-dark);
    background-size: cover;
    background-position: top center;
    background-repeat: no-repeat;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

:root[data-theme="light"] .lp-shot__img { background-image: var(--shot-light); }
@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) .lp-shot__img { background-image: var(--shot-light); }
}


/* --------------------------------------------------------------- tarjetas */

.lp-cards { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); margin-top: var(--space-6); }
.lp-card { padding: var(--space-5); border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); transition: border-color var(--speed) var(--ease), transform var(--speed) var(--ease); }
.lp-card:hover { border-color: var(--border-strong); transform: translateY(-2px); }
.lp-card__icon { font-size: 1.6rem; display: block; }
.lp-card h3 { margin: var(--space-3) 0 0; font-size: var(--step-1); }
.lp-card p { margin: var(--space-2) 0 0; color: var(--text-muted); line-height: 1.6; }

/* --------------------------------------------------------- mision y vision */

.lp-purpose { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); margin-top: var(--space-6); }
.lp-purpose__side { padding: var(--space-6); border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--gradient-soft); }
.lp-purpose__tag { font-size: var(--step--1); text-transform: uppercase; letter-spacing: .1em; font-weight: 600; color: var(--brand-2); }
.lp-purpose__statement { margin: var(--space-4) 0 0; font-size: var(--step-1); font-weight: 600; line-height: 1.4; text-wrap: balance; }
.lp-purpose__detail { margin: var(--space-3) 0 0; color: var(--text-muted); line-height: 1.6; }
.lp-principle {
    margin: var(--space-6) auto 0;
    max-width: 42rem;
    padding: var(--space-5);
    border-left: 3px solid var(--brand-2);
    background: var(--surface);
    border-radius: 0 var(--radius) var(--radius) 0;
    color: var(--text);
    line-height: 1.6;
}

/* ------------------------------------------------------------- cierre CTA */

.lp-cta { position: relative; overflow: hidden; padding: clamp(2.5rem, 6vw, 5rem) 0; border-top: 1px solid var(--border); }
.lp-cta__inner { position: relative; z-index: 1; text-align: center; }
.lp-cta__inner h2 { font-size: var(--step-2); letter-spacing: -0.02em; }
.lp-cta__inner > p { margin: var(--space-3) auto var(--space-6); color: var(--text-muted); max-width: 34rem; }

/* --- Tramadómetro (prefijo .td-) ----------------------------------------------
   El hero es el de la landing (`lp-hero` + `face-wall` + su velo), con otra
   imagen. Lo único propio es que adentro va un formulario: no puede heredar el
   centrado del hero, y encima de una pared de stickers necesita una sombra que
   lo despegue — sin ella el borde de la tarjeta se confunde con los stickers. */
.td-hero__form {
    max-width: 36rem;
    margin: var(--space-6) auto 0;
    text-align: left;
    box-shadow: 0 24px 60px rgb(0 0 0 / 0.35);
    /* Al volver con un error se salta a `#formulario`, y los dos encabezados
       son `sticky`: sin este margen, la caja quedaba pegada arriba y el
       encabezado (63 px en el teléfono, medido) tapaba justo el aviso. 6rem
       cubre el de la landing también, que es un poco más alto. */
    scroll-margin-top: 6rem;
}

/* El resultado, sobre la misma pared que el formulario: las tarjetas con su
   sombra, y lo que queda suelto —el aviso de «es una ayuda», el botón— con
   halo, porque cae encima de los stickers. */
.td-resultado { max-width: 40rem; margin: var(--space-6) auto 0; text-align: left; }
.td-resultado > .card,
.td-resultado > .alert { box-shadow: 0 24px 60px rgb(0 0 0 / 0.35); }
.td-resultado > p { text-shadow: 0 0 2px var(--bg), 0 1px 4px var(--bg), 0 0 12px var(--bg); }

/* La oferta dicha en el formulario: «2 verificaciones gratis», centrada y en
   el color de texto de verdad, para que no se lea como letra chica. */
.td-cupo {
    text-align: center;
    color: var(--text);
    font-size: var(--step-0);
    font-weight: 700;
}

/* El nombre del producto con un sticker pegado encima, girado, como los de la
   pared de fondo. La pregunta baja a segunda línea y a la mitad del tamaño: el
   nombre es la marca, la pregunta es lo que hace. */
.td-hero__name { position: relative; display: inline-block; }

/* El nombre en color de alarma, como un cartel de «¡ojo!». Va en su propio
   span y no en `.td-hero__name`: ahí adentro está el sticker, y el recorte del
   degradado le pintaría las letras. El halo es `drop-shadow` y no
   `text-shadow`, que con texto recortado sale como un borrón detrás. */
.td-hero__alarm {
    background: var(--alarm);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    text-shadow: none;
    filter: drop-shadow(0 2px 12px var(--bg));
}

/* La pregunta, como la pantallita de un medidor —para eso se llama
   tramadómetro—: mono, mayúsculas y SOBRE FONDO SÓLIDO. Suelta sobre la pared
   no se leía: letra chica encima de stickers de colores, y la segunda línea en
   el degradado morado de `lp-hero__title em`, que se perdía entre ellos. Acá
   las dos partes van en `--text` y en una sola línea. */
.td-hero__q {
    display: table;
    margin: var(--space-3) auto 0;
    padding: 0.4em 0.8em;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-full);
    background: color-mix(in srgb, var(--bg) 90%, transparent);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 0.36em;
    font-weight: 700;
    letter-spacing: 0.02em;
    line-height: 1.2;
    text-transform: uppercase;
    text-shadow: none;
}
.td-hero__q em {
    display: inline;
    background: none;
    color: var(--text);
    -webkit-text-fill-color: currentColor;
}
/* Anclado al FINAL del nombre (`left: 100%`) y no a su borde derecho: con
   `right` negativo el sticker tapaba las últimas letras («…metro»), porque su
   ancho depende del texto y del idioma. Así solo monta un poco sobre la «o». */
.td-sticker {
    position: absolute;
    top: -1em;
    left: 100%;
    margin-left: -1.2em;
    padding: 0.45em 0.85em;
    border: 4px solid #fff;
    border-radius: 14px;
    background: #ffd60a;
    color: #111;
    font: 900 clamp(0.8rem, 1.5vw, 1.05rem)/1.05 "Arial Black", Impact, system-ui, sans-serif;
    letter-spacing: 0;
    white-space: nowrap;
    transform: rotate(9deg);
    box-shadow: 0 10px 24px rgb(0 0 0 / 0.4);
    pointer-events: none;
}

/* EL SELLO DEL VEREDICTO: «TRAMA» y «REAL» como el sello de goma de la promo,
   no como una pastilla más. Solo esos dos: «dudoso» y «no se pudo verificar»
   siguen en pastilla, porque un sello afirma y esos dos no afirman nada. El
   color sale de `--bad` y `--ok` para seguir el tema; el desgaste de la tinta
   es un filtro del propio SVG (partials/trama-sello) y el emoji va aparte, como
   texto, en la esquina contraria a la cola. Cada uno de medio lado hacia su
   lado, como en la pieza. */
.td-sello {
    position: relative;
    display: inline-block;
    width: clamp(9.5rem, 30vw, 12.5rem);
    margin: var(--space-2) var(--space-3) var(--space-3);
    line-height: 0;
    transform: rotate(-7deg);
}
/* Rojo y verde de TINTA, los de la pieza —el rojo es el del `.sello` del
   collage— y no `--bad`/`--ok`: esos son pastel, hechos para una pastilla
   sobre fondo tintado, y como tinta sólida sobre una tarjeta blanca el sello
   salía rosado y menta, lavado. Estos dos se leen en los dos temas. */
.td-sello--trama { color: #e11d48; }
.td-sello--real  { color: #16a34a; transform: rotate(5deg); }
.td-sello__svg   { display: block; width: 100%; height: auto; overflow: visible; }
.td-sello__emoji {
    position: absolute;
    top: -0.45em;
    left: -0.4em;
    font-size: clamp(1.6rem, 5vw, 2.1rem);
    line-height: 1;
    transform: rotate(-12deg);
    filter: drop-shadow(0 2px 4px rgb(0 0 0 / 0.35));
}
.td-sello--real .td-sello__emoji { left: auto; right: -0.4em; transform: rotate(12deg); }

/* «🤖 Hecho con IA», al lado del sello cuando la IA está CONFIRMADA (etiqueta
   de la plataforma, marca de agua o la declaración del creador). Morado, de la
   familia del `.sello--morado` del collage: ni el rojo ni el verde del sello,
   porque no es el veredicto sino el porqué. */
.td-etiqueta-ia {
    display: inline-flex;
    align-items: center;
    gap: 0.35em;
    padding: 0.3em 0.75em;
    border: 2px solid currentColor;
    border-radius: var(--radius-full);
    color: #8b5cf6;
    background: color-mix(in srgb, #8b5cf6 12%, transparent);
    font-size: var(--step-0);
    font-weight: 800;
    line-height: 1.2;
    white-space: nowrap;
}

/* La bajada va en el color de texto de verdad, no en el apagado de la landing:
   sobre una pared de stickers de colores, el gris se perdía entre ellos. */
.td-hero .lp-hero__lede { color: var(--text); }

/* EN EL TELÉFONO LA PARED TIENE QUE VERSE.
   El velo de móvil de `lp-hero` es parejo y casi opaco (78–86 %) porque en la
   landing el texto va suelto sobre las caras. Acá lo que más ocupa es el
   formulario, que ya es una tarjeta sólida: el velo puede bajar a la mitad y
   la pared de stickers se ve. Lo que queda suelto —insignia, titular, bajada—
   lleva halo (`lp-hero__lede` ya lo tiene; el titular lo recibe acá, salvo el
   <em>, que es texto recortado y el halo le saldría como un borrón). */
/* Pegado al header, igual que `.hero-xl` en la portada: con el sitio abierto el
   hero cae dentro de `main > .shell` y el aire de arriba de `main` dejaba una
   franja entre el header y la pared. `:first-child` por la misma razón que
   allá: si arriba hay un aviso, la pared no se le sube encima. En el layout de
   la landing no hay `.shell` y nada de esto aplica. */
main > .shell > .td-hero:first-child { margin-top: calc(var(--space-6) * -1); }

@media (max-width: 48rem) {
    /* Y en el teléfono, de borde a borde: `.shell` deja 1rem por lado y la
       pared quedaba recortada en una caja. Se devuelve exactamente ese rem —y
       no con 100vw, que cuenta la barra de desplazamiento y abre un scroll
       lateral—. */
    main > .shell > .td-hero { margin-inline: -1rem; }

    .lp-hero.td-hero::after {
        background:
            linear-gradient(to bottom,
                color-mix(in srgb, var(--bg) 40%, transparent) 0%,
                color-mix(in srgb, var(--bg) 58%, transparent) 30%,
                color-mix(in srgb, var(--bg) 50%, transparent) 70%,
                var(--bg) 100%);
    }

    .td-hero .lp-hero__title { text-shadow: 0 2px 14px var(--bg), 0 0 28px var(--bg); }
    .td-hero .lp-hero__title em { text-shadow: none; }

    /* Un halo más apretado y en capas: con el velo a la mitad, el de la landing
       (difuso, de 10 y 24 px) dejaba asomar los stickers entre las letras. */
    .td-hero .lp-hero__lede {
        font-weight: 500;
        text-shadow: 0 0 2px var(--bg), 0 1px 4px var(--bg), 0 0 12px var(--bg), 0 0 24px var(--bg);
    }

    /* En el teléfono el nombre llena el ancho: el sticker se sube arriba y
       adentro, porque a la derecha lo cortaría el borde de la pantalla. */
    .td-sticker { top: -1.6em; left: auto; right: -0.2em; margin-left: 0; transform: rotate(7deg); }
    .td-hero .td-sticker { text-shadow: none; }
}

/* `.landing-footer` y `.site-footer` se fueron con los tres pies que había:
   ahora hay uno solo, `.ftr`. `wordmark--sm` se queda porque el pie nuevo la
   usa para la marca. */
.wordmark--sm { font-size: var(--step-0); }

/* Quien pidio menos movimiento no ve nada animarse: el cursor, el punto que
   late y el hover de las tarjetas son adorno, no informacion. */
@media (prefers-reduced-motion: reduce) {
    .lp-badge__dot, .lp-searchbox__caret { animation: none; }
    .lp-card:hover { transform: none; }
}

/* El "CR" de qCRew, subrayado con la bandera de Costa Rica.
   -------------------------------------------------------------------------
   La palabra queda SOLIDA y de un solo color. Pintar el CR con un degradado
   tricolor partia "qcrew" en tres pedazos: dos tratamientos multicolor en una
   palabra de cinco letras y el ojo deja de leerla como una sola cosa.
   El subrayado dice Costa Rica sin tocar las letras.

   La bandera son CINCO franjas —azul, blanco, rojo, blanco, azul— y el rojo
   mide el doble que las demas: proporcion 1:1:2:1:1 sobre seis partes. Con
   tres franjas no era la bandera tica, era una bandera cualquiera.

   Van de izquierda a derecha porque el subrayado tiene ~2px de alto: apiladas
   en vertical, como en la bandera real, no se distinguiria ninguna. */
.wordmark__cr {
    position: relative;
    color: var(--text);
    -webkit-text-fill-color: var(--text);
}
.wordmark__cr::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -0.14em;
    height: 0.1em;
    min-height: 2px;
    border-radius: 2px;
    background: linear-gradient(
        90deg,
        var(--cr-azul)   0     16.67%,
        var(--cr-blanco) 16.67% 33.33%,
        var(--cr-rojo)   33.33% 66.67%,
        var(--cr-blanco) 66.67% 83.33%,
        var(--cr-azul)   83.33% 100%
    );
}

/* El wordmark deja de ser degradado: la palabra entera en el color de texto,
   para que el unico acento sea el subrayado. */
.wordmark {
    background: none;
    -webkit-background-clip: border-box;
    background-clip: border-box;
    color: var(--text);
    -webkit-text-fill-color: var(--text);
}

/* Tonos de bandera. En oscuro se suben de luminosidad conservando el matiz: el
   azul oficial (#002b7f) sobre #0d0f14 se ve casi negro. En claro van los
   oficiales, que sobre blanco leen perfecto. */
:root { --cr-azul: #3d7bf0; --cr-rojo: #f0304a; --cr-blanco: #ffffff; }
:root[data-theme="light"] { --cr-azul: #002b7f; --cr-rojo: #ce1126; --cr-blanco: #dfe1ea; }
@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) { --cr-azul: #002b7f; --cr-rojo: #ce1126; --cr-blanco: #dfe1ea; }
}
/* El "CR" de qCRew con los colores de la bandera de Costa Rica.
   El nombre lleva el pais adentro; pintarlo lo dice sin gastar una linea de copy.

   Degradado VERTICAL a proposito: la bandera son franjas horizontales
   (azul-blanco-rojo), asi que de arriba a abajo se lee como la bandera. Uno
   diagonal las cruzaria y perderia la referencia.

   Los tonos NO son los oficiales en tema oscuro: el azul de bandera (#002B7F)
   sobre un fondo #0d0f14 queda practicamente negro y la "C" desaparece. Se sube
   la luminosidad conservando el matiz. En tema claro si van los oficiales. */
.wordmark__cr {
    --cr-azul: #3d7bf0;
    --cr-rojo: #f0304a;
    background: linear-gradient(
        180deg,
        var(--cr-azul) 0%,
        var(--cr-azul) 30%,
        #ffffff 46%,
        #ffffff 56%,
        var(--cr-rojo) 72%,
        var(--cr-rojo) 100%
    );
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

:root[data-theme="light"] .wordmark__cr {
    --cr-azul: #002b7f;
    --cr-rojo: #ce1126;
}
@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) .wordmark__cr {
        --cr-azul: #002b7f;
        --cr-rojo: #ce1126;
    }
}

/* Encabezado del formulario. El bloque entero se lee como una unidad —
   "esto es una lista de lanzamiento"— antes de que aparezca el campo. Sin esto
   el hero muestra un input de correo suelto que no explica nada. */
.lp-form__head { margin-bottom: var(--space-4); text-align: left; }

.lp-form__label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--step--1);
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--brand-2);
}
.lp-form__label-icon { width: 1em; height: .78em; flex: 0 0 auto; }

.lp-form__promise {
    margin: var(--space-2) 0 0;
    font-size: var(--step-0);
    line-height: 1.5;
    color: var(--text);
    text-wrap: pretty;
}

/* ═══════════════════════════════════════════════════════════════════
   Aviso de cookies y paginas legales
   ═══════════════════════════════════════════════════════════════════ */

/* Aviso de cookies -------------------------------------------------- */

.cc {
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: 60;
    padding: var(--space-4);
    /* Sin fondo oscuro sobre la pagina: el aviso informa, no secuestra.
       Un velo que bloquea todo obliga a decidir antes de poder leer de que
       se esta decidiendo. */
    pointer-events: none;
    animation: cc-in 0.35s ease-out both;
}

@keyframes cc-in {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: none; }
}

.cc--gone {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 0.25s ease, transform 0.25s ease;
}

.cc__box {
    pointer-events: auto;
    max-width: 62rem;
    margin-inline: auto;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4) var(--space-5);
    align-items: center;
    justify-content: space-between;
    padding: var(--space-4) var(--space-5);
    background: var(--bg-elevated);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    box-shadow: 0 18px 50px rgb(0 0 0 / 0.35);
}

.cc__text { flex: 1 1 22rem; min-width: 0; }

.cc__title {
    font-weight: 650;
    margin: 0 0 var(--space-2);
}

.cc__body {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--step--1);
    line-height: 1.55;
}

.cc__detail {
    margin: var(--space-2) 0 0;
    color: var(--text-faint);
    font-size: var(--step--1);
}

.cc__detail a { color: var(--text-muted); }

.cc__actions {
    display: flex;
    gap: var(--space-3);
    flex: 0 0 auto;
}

/* Los dos botones son IDENTICOS a proposito. Pintar "aceptar" y apagar
   "rechazar" es el patron oscuro clasico de estos avisos: convierte una
   pregunta en un empujon. Si se pregunta de verdad, las dos respuestas
   cuestan lo mismo. */
.cc__btn {
    font: inherit;
    font-size: var(--step--1);
    font-weight: 550;
    white-space: nowrap;
    padding: var(--space-3) var(--space-5);
    color: var(--text);
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-full);
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.cc__btn:hover  { background: var(--surface-hover); border-color: var(--text-faint); }
.cc__btn:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }

@media (max-width: 34rem) {
    .cc__actions { width: 100%; }
    .cc__btn     { flex: 1; padding-inline: var(--space-3); }
}

/* Quien pidio menos animacion no ve el aviso deslizarse. */
@media (prefers-reduced-motion: reduce) {
    .cc, .cc--gone { animation: none; transition: none; }
}

/* Enlaces legales del pie ------------------------------------------- */

.legal-links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    align-items: center;
    margin: var(--space-3) 0 0;
    color: var(--text-faint);
}

.legal-links a { color: var(--text-faint); text-decoration: none; }
.legal-links a:hover { color: var(--text-muted); text-decoration: underline; }

/* El boton de "cambiar mis cookies" es un submit dentro de un form, pero
   tiene que leerse como un enlace mas: es la misma clase de accion que los
   otros tres, y un boton gris al lado de tres enlaces parece otra cosa. */
.legal-links__reset { display: inline; }

.legal-links__reset button {
    font: inherit;
    font-size: inherit;
    color: var(--text-faint);
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
}

.legal-links__reset button:hover { color: var(--text-muted); text-decoration: underline; }

/* Paginas legales ---------------------------------------------------- */

.legal {
    /* 68ch: el ancho al que un texto largo se lee sin que el ojo se pierda
       al saltar de renglon. Un documento legal a todo lo ancho de la
       pantalla es la forma mas facil de que nadie lo lea. */
    max-width: 68ch;
    margin-inline: auto;
    padding: var(--space-8) var(--space-5) var(--space-8);
}

.legal__head { margin-bottom: var(--space-7); }

.legal__title {
    font-size: var(--step-3);
    line-height: 1.1;
    margin: 0 0 var(--space-3);
    letter-spacing: -0.02em;
}

.legal__lede {
    font-size: var(--step-1);
    color: var(--text-muted);
    margin: 0 0 var(--space-4);
    line-height: 1.5;
}

.legal__updated {
    font-size: var(--step--1);
    color: var(--text-faint);
    margin: 0;
}

.legal__section {
    margin-bottom: var(--space-6);
    /* Contador para numerar las secciones desde CSS: agregar una en medio
       no obliga a renumerar todas las de abajo, en dos idiomas. */
    counter-increment: legal-section;
}

.legal { counter-reset: legal-section; }

.legal__h2 {
    font-size: var(--step-1);
    margin: 0 0 var(--space-3);
    display: flex;
    gap: var(--space-3);
    align-items: baseline;
}

.legal__h2::before {
    content: counter(legal-section);
    font-size: var(--step--1);
    font-variant-numeric: tabular-nums;
    color: var(--text-faint);
    flex: 0 0 auto;
}

.legal__section p {
    margin: 0 0 var(--space-3);
    color: var(--text-muted);
    line-height: 1.65;
}

.legal__foot {
    margin-top: var(--space-8);
    padding-top: var(--space-5);
    border-top: 1px solid var(--border);
}

.legal__otherLabel {
    font-size: var(--step--1);
    color: var(--text-faint);
    margin: 0 0 var(--space-2);
}

.legal__links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin: 0;
}

/* ==========================================================================
   Noticias — /es/noticias y /en/news
   --------------------------------------------------------------------------
   Una sección de prensa dentro del mismo sistema: sin fuentes nuevas, sin
   librerías, y con los mismos tokens que el resto. Lo que la hace "de noticias"
   no es un color propio sino la jerarquía — banner, antetítulo, medida de
   lectura corta y citas destacadas.

   Se pinta dentro de DOS shells distintos (`layouts/landing` con el modo coming
   soon, `layouts/main` con el sitio abierto), así que nada de acá depende de en
   cuál esté: `.news-shell` centra por su cuenta en los dos casos.
   ========================================================================== */

.news-shell {
    width: min(100% - 2rem, 1080px);
    margin-inline: auto;
}

/* --------------------------------------------------------------- encabezado */

.news-head { margin-bottom: var(--space-6); }

.news-head__title {
    font-size: var(--step-3);
    letter-spacing: -0.03em;
    line-height: 1.05;
    margin: 0;
}

.news-head__lede {
    margin: var(--space-3) 0 0;
    color: var(--text-muted);
    font-size: var(--step-1);
    max-width: 46ch;
    line-height: 1.5;
}

/* El antetítulo. Es la etiqueta que le dice al ojo "esto es una noticia y va
   de esto" antes de leer el titular. */
.news-kicker {
    display: inline-block;
    font-size: var(--step--1);
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--brand-1);
    margin-bottom: var(--space-2);
}

.news-kicker--on-image {
    color: #fff;
    background: var(--gradient);
    padding: 0.25rem 0.6rem;
    border-radius: var(--radius-full);
    letter-spacing: 0.08em;
}

/* ------------------------------------------------------------------ banner */

/* LA FORMA LA MANDA LA PORTADA, NO EL LARGO DEL TITULAR.
   Sin `aspect-ratio` la caja crecía con el texto: un titular de 130 caracteres
   en móvil la estiraba a 326x647 —razón 0,50— y `object-fit: cover` recortaba
   una portada de 1280x720 a una tira vertical donde no se reconocía nada. La
   razón fija deja que la foto se vea entera; si el texto no cupiera, la caja
   crece igual, y para eso está el degradado anclado en px de más abajo. */
.news-banner {
    position: relative;
    display: block;
    border-radius: var(--radius-lg);
    overflow: hidden;
    aspect-ratio: 16 / 9;
    min-height: clamp(300px, 42vw, 480px);
    background: var(--surface-2);
    box-shadow: var(--shadow);
    text-decoration: none;
    color: #fff;
    isolation: isolate;
}

.news-banner__img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* El zoom lento al pasar por encima es el único movimiento de la sección.
       Se apaga entero más abajo si el visitante pidió menos animación. */
    transition: transform 600ms var(--ease);
}

.news-banner:hover .news-banner__img { transform: scale(1.04); }

/* El degradado NO es decoración: sin él, un titular blanco sobre una foto
   clara deja de leerse. Va como capa propia para que el zoom de la imagen no
   lo arrastre.

   LAS PARADAS VAN EN px, NO EN %. Con porcentajes el degradado se estira con
   la caja: el titular arranca al 14-16% de arriba, o sea al 84% contando desde
   abajo, y ahí la versión anterior tenía 8-24% de negro — nada. La miniatura de
   «PASTA E PATATE» tiene una franja amarilla justo en esa altura y el titular
   blanco desapareció encima, en móvil Y en escritorio. Anclado en px, los 560px
   de abajo están siempre oscuros, que es exactamente donde vive el texto por el
   `justify-content: flex-end` del inner. */
.news-banner__scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(
        to top,
        rgb(6 8 14 / 94%) 0,
        rgb(6 8 14 / 88%) 200px,
        rgb(6 8 14 / 72%) 420px,
        rgb(6 8 14 / 38%) 620px,
        rgb(6 8 14 / 14%) 100%
    );
}

.news-banner__inner {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-height: 100%;
    padding: clamp(1.25rem, 3vw, 2.5rem);
    gap: var(--space-3);
}

/* La píldora del antetítulo mide lo que dice, no lo que mide la caja. En una
   columna flex el `align-items: stretch` por defecto la estiraba de borde a
   borde y parecía una barra de color, no una etiqueta. Va acá y no en
   `.news-kicker--on-image` porque es un problema del contenedor. */
.news-banner .news-kicker { align-self: flex-start; }

.news-banner__title {
    margin: 0;
    font-size: var(--step-3);
    line-height: 1.06;
    letter-spacing: -0.03em;
    /* 20ch dejaba casi la mitad del banner sin usar y empujaba cualquier
       titular real a seis o siete líneas, hasta que el bloque de texto no cabía
       en la parte oscura del degradado. 24ch sigue siendo una medida de lectura
       cómoda a este tamaño y le quita dos líneas al mismo titular. */
    max-width: 24ch;
    text-wrap: balance;
    /* Sombra corta y baja opacidad: sostiene el texto sobre las zonas claras
       de la foto sin que se vea como un efecto. */
    text-shadow: 0 2px 18px rgb(0 0 0 / 45%);
}

.news-banner__excerpt {
    margin: 0;
    max-width: 58ch;
    color: rgb(255 255 255 / 88%);
    font-size: var(--step-0);
    line-height: 1.55;
    /* Tres líneas, como en `.news-card__excerpt` y por un motivo más fuerte
       acá: la entradilla puede traer 320 caracteres, y sin cortarla el bloque
       de texto crece hasta salirse del degradado y volver a taparle la foto. */
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.news-banner__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--step--1);
    color: rgb(255 255 255 / 78%);
}

.news-banner__cta {
    margin-left: auto;
    font-weight: 700;
    color: #fff;
}

.news-banner__credit {
    margin: var(--space-2) 0 0;
    font-size: var(--step--1);
    color: var(--text-faint);
    text-align: right;
}

.news-banner__credit a { color: var(--text-muted); }

/* EN PANTALLA ANGOSTA EL BANNER SE APILA, COMO LAS TARJETAS DE ABAJO.
   Poner el texto ENCIMA de la foto es un patrón de banner ancho y bajo. En un
   teléfono de 390px el bloque de texto solo —antetítulo, titular de ocho
   líneas, entradilla y firma— pasa los 500px de alto: no hay foto que lo
   aguante debajo y no hay degradado que lo salve. Apilar es lo que hacen NYT,
   The Guardian y BBC con su nota principal en móvil, y es lo que ya hace
   `.news-card` en esta misma página unos centímetros más abajo — o sea que no
   se inventa una forma nueva, se usa la que la página ya tiene. */
@media (max-width: 62rem) {
    .news-banner {
        aspect-ratio: auto;
        min-height: 0;
        background: var(--surface);
        border: 1px solid var(--border);
        /* El texto deja de ser blanco sobre foto y vuelve al color normal. */
        color: inherit;
    }

    .news-banner__img {
        position: static;
        aspect-ratio: 16 / 9;
        height: auto;
    }

    /* Sin texto encima, el degradado solo le ensuciaría la foto. */
    .news-banner__scrim { display: none; }

    .news-banner__inner {
        min-height: 0;
        padding: var(--space-4);
        gap: var(--space-2);
    }

    .news-banner__title {
        font-size: var(--step-2);
        max-width: none;
        text-shadow: none;
    }

    .news-banner__excerpt {
        color: var(--text-muted);
        -webkit-line-clamp: 4;
    }

    .news-banner__meta { color: var(--text-faint); }
    .news-banner__cta { color: var(--brand-1); }
}

/* ------------------------------------------------------------------ listado */

.news-grid {
    display: grid;
    gap: var(--space-5);
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
    margin-top: var(--space-6);
}

.news-card { display: flex; flex-direction: column; overflow: hidden; }

.news-card__thumb {
    aspect-ratio: 16 / 9;
    background: var(--surface-2);
    display: grid;
    place-items: center;
    overflow: hidden;
}

.news-card__thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 400ms var(--ease);
}

.news-card:hover .news-card__thumb img { transform: scale(1.05); }

.news-card__glyph { font-size: var(--step-2); color: var(--text-faint); }

.news-card__body {
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    flex: 1;
}

.news-card__title {
    margin: 0;
    font-size: var(--step-1);
    line-height: 1.2;
    letter-spacing: -0.015em;
    text-wrap: balance;
}

.news-card__excerpt {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--step--1);
    line-height: 1.55;
    /* Tres líneas y corta. Tarjetas de alto distinto en una grilla se ven como
       un error de maquetación, no como variedad. */
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.news-card__meta {
    margin-top: auto;
    padding-top: var(--space-2);
    display: flex;
    gap: var(--space-2);
    align-items: center;
    font-size: var(--step--1);
    color: var(--text-faint);
}

.news-empty { text-align: center; padding: var(--space-7) var(--space-5); }
.news-empty__title { font-size: var(--step-1); margin: 0 0 var(--space-2); }

/* ----------------------------------------------------------------- artículo */

.news-crumb { margin-bottom: var(--space-5); font-size: var(--step--1); }
.news-crumb a { color: var(--text-muted); }

.news-article { max-width: 720px; margin-inline: auto; }

.news-article__head { margin-bottom: var(--space-6); }

.news-article__title {
    margin: 0;
    font-size: var(--step-3);
    line-height: 1.06;
    letter-spacing: -0.035em;
    text-wrap: balance;
}

.news-article__lede {
    margin: var(--space-4) 0 0;
    font-size: var(--step-1);
    line-height: 1.5;
    color: var(--text-muted);
}

.news-article__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-5);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border);
    font-size: var(--step--1);
    color: var(--text-faint);
}

.news-byline { color: var(--text-muted); }
.news-byline strong { color: var(--text); }
.news-byline__mail { color: var(--text-faint); margin-left: 0.4rem; }

.news-article__updated { margin: var(--space-2) 0 0; }
.news-article__notice { margin-top: var(--space-4); }

/* La portada rompe la medida de lectura y llega al ancho de la sección: es la
   única pieza del artículo que puede, y por eso funciona como respiro. */
.news-article__cover {
    margin: 0 0 var(--space-6);
    width: min(100vw - 2rem, 1080px);
    margin-inline: calc(50% - min(100vw - 2rem, 1080px) / 2);
}

.news-article__cover img {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
}

.news-article__cover figcaption,
.news-shot__caption {
    font-size: var(--step--1);
    color: var(--text-faint);
    margin-top: var(--space-2);
}

.news-article__cover figcaption a { color: var(--text-muted); }

/* --------------------------------------------------------------- el cuerpo */

.news-body { font-size: 1.06rem; line-height: 1.75; }

.news-body p {
    margin: 0 0 var(--space-5);
    color: var(--text);
}

/* La entradilla del cuerpo un punto más grande: engancha sin necesitar una
   letra capital, que en pantalla casi siempre queda mal. */
.news-body > p:first-child {
    font-size: 1.16rem;
    color: var(--text);
}

.news-body__h2 {
    font-size: var(--step-2);
    line-height: 1.15;
    letter-spacing: -0.02em;
    margin: var(--space-7) 0 var(--space-4);
    text-wrap: balance;
}

.news-body__list {
    margin: 0 0 var(--space-5);
    padding-left: 1.2rem;
    color: var(--text);
}

.news-body__list li { margin-bottom: var(--space-2); }

.news-body a {
    color: var(--brand-1);
    text-underline-offset: 3px;
}

.news-quote {
    margin: var(--space-6) 0;
    padding: var(--space-5) var(--space-5) var(--space-5) var(--space-6);
    border-left: 3px solid transparent;
    /* El borde es el degradado de la marca. Con `border-image` y no con un
       pseudo-elemento para que siga al alto real del texto. */
    border-image: var(--gradient) 1;
    background: var(--gradient-soft);
    border-radius: 0 var(--radius) var(--radius) 0;
}

.news-quote p {
    margin: 0;
    font-size: var(--step-1);
    line-height: 1.45;
    letter-spacing: -0.01em;
}

.news-quote cite {
    display: block;
    margin-top: var(--space-3);
    font-size: var(--step--1);
    font-style: normal;
    color: var(--text-muted);
}

.news-quote cite::before { content: "— "; }

/* ---------------------------------------------------------------- galería */

.news-gallery { margin: var(--space-7) 0; }

.news-gallery__title { font-size: var(--step-2); margin: 0 0 var(--space-2); }
.news-gallery__note { margin: 0 0 var(--space-4); }

.news-gallery__grid {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr));
}

.news-shot {
    display: block;
    margin: 0;
    text-decoration: none;
    color: inherit;
}

.news-shot img {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--radius);
    background: var(--surface-2);
    transition: transform 300ms var(--ease), box-shadow 300ms var(--ease);
}

a.news-shot:hover img {
    transform: translateY(-3px);
    box-shadow: var(--shadow);
}

.news-shot__caption { display: block; }
.news-shot__title { display: block; color: var(--text-muted); }
.news-shot__credit { display: block; color: var(--text-faint); }

/* ----------------------------------------------------------- ficha y cierre */

.news-creator {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    margin: var(--space-6) 0;
}

.news-creator__id { display: flex; align-items: center; gap: var(--space-4); }

.news-creator__avatar {
    width: 64px;
    height: 64px;
    border-radius: var(--radius-full);
    object-fit: cover;
    flex: 0 0 auto;
}

.news-creator__avatar--initials {
    display: grid;
    place-items: center;
    background: var(--gradient);
    color: #fff;
    font-weight: 700;
}

.news-creator__label { margin: 0; }
.news-creator__name { margin: 0.1rem 0 0; font-size: var(--step-1); font-weight: 700; }

.news-share {
    margin-top: var(--space-7);
    padding-top: var(--space-5);
    border-top: 1px solid var(--border);
}

.news-share__title { font-size: var(--step-1); margin: 0 0 var(--space-2); }

.news-more { margin-top: var(--space-8); }

/* -------------------------------------------------- el enlace en la landing */

.landing-nav {
    display: flex;
    gap: var(--space-4);
    margin-left: var(--space-5);
    font-size: var(--step-0);
}

.landing-nav a {
    color: var(--text-muted);
    text-decoration: none;
    font-weight: 600;
    padding: 0.35rem 0;
    border-bottom: 2px solid transparent;
}

.landing-nav a:hover { color: var(--text); }

.landing-nav a[aria-current="page"] {
    color: var(--text);
    border-bottom-color: var(--brand-1);
}

/* ------------------------------------------------------- panel de redacción */

.admin-preview { margin: var(--space-4) 0 0; }

.admin-preview img {
    width: 100%;
    max-width: 420px;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--radius);
    border: 1px solid var(--border);
    display: block;
}

.admin-help {
    margin: var(--space-2) 0 0;
    padding: var(--space-3);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: var(--step--1);
    color: var(--text-muted);
    white-space: pre-wrap;
    overflow-x: auto;
}

.admin-gallery {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr));
    margin-top: var(--space-4);
}

.admin-gallery__item { margin: 0; }

.admin-gallery__item img {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    display: block;
}

.admin-gallery__item figcaption {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-top: var(--space-2);
    color: var(--text-muted);
}

/* Quien pidió menos movimiento no ve ninguno: es una preferencia del sistema
   operativo, no una sugerencia. */
@media (prefers-reduced-motion: reduce) {
    .news-banner__img,
    .news-card__thumb img,
    .news-shot img {
        transition: none;
    }

    .news-banner:hover .news-banner__img,
    .news-card:hover .news-card__thumb img,
    a.news-shot:hover img {
        transform: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════
   Descubrir: buscador con facetas
   ═══════════════════════════════════════════════════════════════════ */

.dsc { display: block; }

/* Encabezado ---------------------------------------------------------- */

.dsc-head { margin-bottom: var(--space-4); }

.dsc-head__title {
    font-size: var(--step-2);
    letter-spacing: -0.03em;
    line-height: 1.1;
    margin: 0;
}

/* Caja de búsqueda ---------------------------------------------------- */

.dsc-search { position: relative; margin-bottom: var(--space-5); }

.dsc-search__box {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
    background: var(--bg-elevated);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-full);
    transition: border-color 0.15s ease;
}

.dsc-search__box:focus-within { border-color: var(--text-faint); }

.dsc-search__icon { color: var(--text-faint); font-size: var(--step-1); flex: 0 0 auto; }

.dsc-search__input {
    flex: 1 1 auto;
    min-width: 0;
    font: inherit;
    font-size: var(--step-1);
    color: var(--text);
    background: none;
    border: 0;
    padding: var(--space-2) 0;
}
.dsc-search__input:focus { outline: none; }
.dsc-search__input::placeholder { color: var(--text-faint); }
/* La "x" nativa de Safari/Chrome sobre un fondo redondeado se ve cortada. */
.dsc-search__input::-webkit-search-cancel-button { -webkit-appearance: none; }

.dsc-search__go { flex: 0 0 auto; border-radius: var(--radius-full); }

.dsc-search__hint { margin: var(--space-2) 0 0 var(--space-4); }

/* Sugerencias --------------------------------------------------------- */

.dsc-sug {
    position: absolute;
    z-index: 40;
    inset-inline: 0;
    top: calc(100% + var(--space-2));
    margin: 0;
    padding: var(--space-2);
    list-style: none;
    background: var(--bg-elevated);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    box-shadow: 0 18px 44px rgb(0 0 0 / 0.3);
    max-height: 60vh;
    overflow-y: auto;
}

.dsc-sug__head {
    font-size: var(--step--1);
    color: var(--text-faint);
    padding: var(--space-2) var(--space-3) var(--space-1);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.dsc-sug__item {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.dsc-sug__item:hover { background: var(--surface-hover); }

.dsc-sug__n,
.dsc-sug__crumb { font-size: var(--step--1); color: var(--text-faint); }

/* Chips de filtros activos -------------------------------------------- */

.dsc-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
    margin-bottom: var(--space-5);
}

.dsc-chips__label { flex: 0 0 auto; }

.dsc-chip {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    font-size: var(--step--1);
    color: var(--text);
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-full);
    text-decoration: none;
}
.dsc-chip:hover { background: var(--surface-hover); }

/* La etiqueta de tipo ("zona", "tema") es lo que hace legible el chip:
   sin ella, "Cartago" y "Comida" se ven como la misma clase de filtro. */
.dsc-chip__kind {
    font-size: 0.7em;
    color: var(--text-faint);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.dsc-chip__x { color: var(--text-faint); font-size: 1.1em; line-height: 1; }
.dsc-chip:hover .dsc-chip__x { color: var(--text); }

/* El de texto libre no se puede quitar por separado: sale de la frase, y
   quitarlo sería reescribirle la búsqueda a la persona. Por eso no es enlace. */
.dsc-chip--text { cursor: default; }

.dsc-chips__clear { color: var(--text-faint); margin-inline-start: var(--space-2); }

/* Dos columnas -------------------------------------------------------- */

.dsc-body {
    display: grid;
    grid-template-columns: 15rem minmax(0, 1fr);
    gap: var(--space-6);
    align-items: start;
}

@media (max-width: 62rem) {
    /* En móvil los filtros no van encima de los resultados: viven en un panel que
       sube desde abajo detrás de un botón, como Amazon, Airbnb y Booking. El
       protagonista del teléfono es el resultado; el filtro se abre cuando se
       necesita. Dentro del panel, cada faceta es una tarjeta plegable. */
    .dsc-body { grid-template-columns: 1fr; gap: var(--space-5); }

    /* El botón que abre el panel, con el número de filtros puestos. */
    .dsc-filtros-btn {
        display: inline-flex;
        align-items: center;
        gap: var(--space-2);
        align-self: flex-start;
        padding: var(--space-2) var(--space-4);
        border: 1px solid var(--border);
        border-radius: var(--radius-full);
        background: var(--surface);
        color: var(--text);
        font-weight: 600;
        font-size: var(--step--1);
        cursor: pointer;
    }
    .dsc-filtros-btn__n {
        display: inline-grid;
        place-items: center;
        min-width: 1.5em;
        height: 1.5em;
        padding: 0 0.4em;
        border-radius: var(--radius-full);
        background: var(--primary);
        color: #fff;
        font-size: 0.85em;
        font-variant-numeric: tabular-nums;
    }

    /* El velo detrás del panel. */
    .dsc-facets-backdrop {
        position: fixed;
        inset: 0;
        z-index: 60;
        background: rgba(0, 0, 0, 0.5);
        opacity: 0;
        transition: opacity 0.25s var(--ease);
    }
    .dsc-facets-backdrop.is-open { opacity: 1; }

    /* El panel. Sube desde abajo; cabecera y pie fijos, facetas con scroll. */
    .dsc-facets {
        position: fixed;
        inset: auto 0 0 0;
        z-index: 61;
        height: 85dvh;
        overflow-y: auto;
        /* El scroll del panel NO arrastra la página de atrás: sin esto, al llegar
           al final de la lista el dedo empezaba a mover el fondo — el «scroll
           difícil» de un panel que pelea con la página que tiene debajo. */
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
        padding: 0 var(--space-4) var(--space-4);
        background: var(--bg-elevated);
        border-radius: var(--radius-lg) var(--radius-lg) 0 0;
        box-shadow: 0 -20px 44px -20px rgba(0, 0, 0, 0.6);
        transform: translateY(100%);
        transition: transform 0.28s var(--ease);
    }
    .dsc-facets.is-open { transform: translateY(0); }

    /* Con el panel abierto, la página de atrás queda fija: el dedo mueve la lista
       de filtros, no el fondo. */
    body.filtros-abiertos { overflow: hidden; }

    .dsc-facets__head {
        position: sticky;
        top: 0;
        z-index: 1;
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-3);
        padding: var(--space-4) 0 var(--space-3);
        margin-bottom: var(--space-3);
        background: var(--bg-elevated);
        border-bottom: 1px solid var(--border);
    }
    .dsc-facets__ttl { font-weight: 700; font-size: var(--step-0); color: var(--text); }
    .dsc-facets__x {
        display: grid;
        place-items: center;
        width: 2rem;
        height: 2rem;
        border: 0;
        border-radius: var(--radius-full);
        background: none;
        color: var(--text-muted);
        font-size: 1.05rem;
        cursor: pointer;
    }
    .dsc-facets__x:hover { background: var(--surface-hover); }

    .dsc-facets__foot {
        position: sticky;
        bottom: 0;
        padding: var(--space-3) 0 0;
        margin-top: var(--space-3);
        background: var(--bg-elevated);
        border-top: 1px solid var(--border);
    }

    /* Cada faceta: una tarjeta plegable. */
    .dsc-facet {
        min-width: 0;
        margin-bottom: var(--space-3);
        border: 1px solid var(--border);
        border-radius: var(--radius);
        background: var(--surface);
        padding: 0 var(--space-3);
    }
    .dsc-facet__title { margin: 0; padding: var(--space-3) 0; }
    /* El chevron. Gira al abrir. */
    .dsc-facet__title::after {
        content: "";
        width: 0.5rem;
        height: 0.5rem;
        border-right: 2px solid currentColor;
        border-bottom: 2px solid currentColor;
        transform: rotate(45deg);
        transition: transform var(--speed) var(--ease);
        opacity: 0.7;
        margin: 0 2px 0 var(--space-2);
    }
    .dsc-facet[open] > .dsc-facet__title::after { transform: rotate(-135deg); }
    .dsc-facet[open] { padding-bottom: var(--space-3); }
    .dsc-facet__list { margin-top: var(--space-1); }
    /* Los renglones de opción, con más aire para el dedo. */
    .dsc-opt { padding: var(--space-2); }
}

/* En escritorio nada del panel de móvil existe: ni el botón, ni la cabecera, ni
   el pie, ni el velo. La faceta se ve entera SIEMPRE, esté «open» o no el
   <details>, y sin chevron ni cursor de plegar — la barra lateral de siempre. */
@media (min-width: 62.0625rem) {
    .dsc-filtros-btn,
    .dsc-facets__head,
    .dsc-facets__foot,
    .dsc-facets-backdrop { display: none; }

    details.dsc-facet > .dsc-facet__list,
    details.dsc-facet > .dsc-facet__note,
    details.dsc-facet > .dsc-facet__up { display: block; }
    .dsc-facet__title { cursor: default; pointer-events: none; }
    .dsc-facet__title::after { display: none; }

    /* La barra lateral fija de escritorio. Va AQUÍ y no suelta al final: una
       regla `.dsc-facets` sin media, escrita después del bloque de móvil, le
       ganaba a `position: fixed` por orden de cascada y dejaba el panel ocupando
       85dvh en el flujo — el hueco negro gigante sobre los resultados. */
    .dsc-facets { position: sticky; top: var(--space-4); }
    .dsc-facet { margin-bottom: var(--space-6); }
}

.dsc-facet__title {
    font-size: var(--step--1);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-faint);
    margin: 0 0 var(--space-3);
    font-weight: 600;
    /* Es un <summary>: fila con el chevron a la derecha, sin el triángulo nativo
       (que en Safari y Firefox se dibuja distinto y descuadra el título). */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    list-style: none;
    -webkit-tap-highlight-color: transparent;
}
.dsc-facet__title::-webkit-details-marker { display: none; }

.dsc-facet__up { display: block; color: var(--text-muted); margin-bottom: var(--space-2); }

.dsc-facet__list { list-style: none; margin: 0; padding: 0; }

.dsc-facet__note { margin: var(--space-3) 0 0; line-height: 1.4; }

.dsc-opt {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    text-decoration: none;
    font-size: var(--step--1);
}
.dsc-opt:hover { background: var(--surface-hover); color: var(--text); }

.dsc-opt--on {
    background: var(--surface-2);
    color: var(--text);
    font-weight: 600;
}

.dsc-opt__name { display: flex; gap: var(--space-2); align-items: baseline; min-width: 0; }
.dsc-opt__ico { color: var(--text-faint); }

/* El conteo es la mitad del valor de una faceta: sin él, elegir es adivinar. */
.dsc-opt__n {
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    color: var(--text-faint);
    font-size: 0.9em;
}

/* Resultados ---------------------------------------------------------- */

/* La columna de resultados. Existía en el marcado sin ninguna regla: el ancho
   se lo daba `minmax(0, 1fr)` de .dsc-body y nadie lo notaba, pero una clase
   sin definición es la que aparece en un diff futuro como si sobrara. El
   `min-width: 0` lo declara acá también, que es donde se lee: sin él una tabla
   o un bloque de código anchos empujan la cuadrícula fuera de la pantalla. */
.dsc-results { min-width: 0; }

.dsc-results__head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: var(--space-4);
}
.dsc-results__head p { margin: 0; }

.dsc-sort select { font-size: var(--step--1); }

/* Salidas de la pantalla vacía ---------------------------------------- */

.dsc-outs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    justify-content: center;
    margin-top: var(--space-5);
}

.dsc-out {
    padding: var(--space-2) var(--space-4);
    font-size: var(--step--1);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    color: var(--text-muted);
    text-decoration: none;
}
.dsc-out:hover { background: var(--surface-hover); color: var(--text); }

/* Aviso de búsqueda ampliada. Ámbar y no rojo: no es un error, es una
   decisión que tomamos por la persona y que tiene que poder ver. */
.dsc-relaxed {
    margin: 0 0 var(--space-4);
    padding: var(--space-3) var(--space-4);
    background: var(--surface-2);
    border-inline-start: 3px solid var(--warn, #d0a215);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
}

/* Aviso del filtro de año. Discreto: informa sin competir con los resultados. */
.dsc-anio {
    margin: 0 0 var(--space-4);
    color: var(--text-muted);
}
.dsc-anio a { color: var(--text); text-decoration: underline; }
.dsc-anio .faint { display: block; margin-top: var(--space-1); color: var(--text-faint); }

/* ── Resultados de contenido: un creador con sus videos ─────────────── */

.dsc-videos { display: grid; gap: var(--space-5); margin-bottom: var(--space-6); }
.dsc-otros-wrap { margin: var(--space-6) 0 var(--space-3); }
.dsc-otros {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: 100%;
    padding: 0.7rem 1.1rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    background: var(--surface-2);
    color: var(--text);
    font-weight: 600;
    font-size: var(--step--1);
    user-select: none;
    transition: background 0.15s ease, border-color 0.15s ease;
}
.dsc-otros:hover { background: var(--surface-hover); border-color: var(--border-strong); }
.dsc-otros:focus-visible { outline: 2px solid var(--accent, var(--border-strong)); outline-offset: 2px; }
.dsc-otros::-webkit-details-marker { display: none; }
.dsc-otros__chev { font-size: 0.85em; transition: transform 0.2s ease; }
.dsc-otros-wrap[open] .dsc-otros__chev { transform: rotate(180deg); }
.dsc-otros-wrap[open] > .grid { margin-top: var(--space-4); }

.rg {
    padding: var(--space-4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.rg__head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}

.rg__creator { display: flex; align-items: center; gap: var(--space-3); text-decoration: none; min-width: 0; }

.rg__avatar {
    width: 2rem; height: 2rem;
    border-radius: var(--radius-full);
    object-fit: cover;
    flex: 0 0 auto;
}

.rg__avatar--letra {
    display: grid;
    place-items: center;
    background: var(--surface-2);
    color: var(--text-muted);
    font-weight: 600;
}

.rg__nombre { font-weight: 600; color: var(--text); }
.rg__n { flex: 0 0 auto; }

/* Cuatro por fila en escritorio, dos en móvil: una miniatura más chica que
   esto no deja leer el título, que es lo que decide el clic. */
.rg__videos {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--space-3);
}

@media (max-width: 48rem) {
    .rg__videos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.rg__video { display: block; text-decoration: none; color: var(--text-muted); }
.rg__video:hover { color: var(--text); }

.rg__thumb {
    display: grid;
    place-items: center;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background: var(--surface-2);
    border-radius: var(--radius-sm);
    margin-bottom: var(--space-2);
}

.rg__thumb img { width: 100%; height: 100%; object-fit: cover; }

.rg__titulo {
    display: block;
    font-size: var(--step--1);
    line-height: 1.35;
}

.rg__why { display: block; margin-top: var(--space-1); }

.rg__mas { display: inline-block; margin-top: var(--space-3); color: var(--text-muted); }

/* ── Barra de suplantación ───────────────────────────────────────────
   Fija y llamativa a propósito: el peor escenario de esta función no es
   el abuso, es el OLVIDO — alguien entra a revisar algo y media hora
   después escribe creyendo que es él mismo. */

.imp-bar {
    position: sticky;
    top: 0;
    z-index: 70;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-4);
    background: #7c2d12;
    color: #fff7ed;
    font-size: var(--step--1);
}

.imp-bar__dot {
    width: .6rem; height: .6rem;
    border-radius: var(--radius-full);
    background: #fb923c;
    flex: 0 0 auto;
    animation: imp-latido 2s ease-in-out infinite;
}

@keyframes imp-latido { 0%,100% { opacity: 1 } 50% { opacity: .35 } }
@media (prefers-reduced-motion: reduce) { .imp-bar__dot { animation: none } }

.imp-bar__txt { flex: 1 1 auto; min-width: 0; }
.imp-bar__time { opacity: .8; margin-inline-start: var(--space-2); }

.imp-bar__out {
    font: inherit;
    font-weight: 600;
    padding: var(--space-1) var(--space-3);
    color: #7c2d12;
    background: #fff7ed;
    border: 0;
    border-radius: var(--radius-full);
    cursor: pointer;
    flex: 0 0 auto;
}
.imp-bar__out:hover { background: #fed7aa; }

/* ── Cabecera dentro del estudio y del panel ─────────────────────────
   El menú público desaparece: quien está editando su perfil o revisando
   leads no está navegando el sitio, y dos menús a la vez obligan a leer
   los dos para saber cuál lleva a dónde. */

.site-header--app .site-header__inner { gap: var(--space-4); }

/* El nombre de la sección, pegado a la marca: dice dónde estás sin
   competir con nada. La barra vertical lo separa de la marca sin sumar
   otro elemento que mirar. */
.site-header__seccion {
    font-size: var(--step--1);
    font-weight: 600;
    color: var(--text-muted);
    padding-inline-start: var(--space-3);
    border-inline-start: 1px solid var(--border-strong);
    white-space: nowrap;
}

/* La salida usa `.btn btn--sm` (secundario), como el resto de los CTA del panel:
   antes era un enlace suelto que se leía como texto y no como acción. */
@media (max-width: 40rem) {
    /* En móvil la sección se cae antes que la salida: saber dónde estás
       lo dice la barra lateral; poder salir no lo dice nada más. */
    .site-header__seccion { display: none; }
}


/* -------------------------------------------------- 22. Category picker */

/* Las 68 etiquetas del estudio: buscador arriba, ramas plegables abajo.
   Antes eran una parrilla plana sin buscador ni títulos, y encontrar una
   era leer la lista entera. */

.catpick { display: grid; gap: var(--space-3); }

/* Título a la izquierda, contador a la derecha. `flex-start` y no `center`
   porque el bloque de la izquierda son dos líneas y el contador tiene que
   quedar a la altura del título, no del subtítulo. */
.catpick__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}

/* Cuántas etiquetas llevás. Es el número que gobierna toda la pantalla —al
   llegar al tope se apagan las casillas— así que va arriba y en grande, no
   como una nota gris al lado del buscador. */
.cat-tally {
    display: grid;
    justify-items: center;
    gap: 0.1rem;
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-2);
    line-height: 1;
    flex-shrink: 0;
    transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}

.cat-tally__num { font-size: var(--step-2); font-weight: 700; letter-spacing: -0.02em; }
.cat-tally__num strong { color: var(--brand-1); font-weight: 700; }
.cat-tally__max { color: var(--text-faint); font-weight: 600; }

.cat-tally__lbl {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    font-weight: 600;
}

/* Lleno: ámbar, no rojo. No es un error — es el plan diciendo hasta dónde
   llega, y el rojo mandaría a buscar qué se hizo mal. */
.catpick--full .cat-tally {
    border-color: var(--warn);
    background: color-mix(in srgb, var(--warn) 12%, transparent);
}

.catpick--full .cat-tally__num strong,
.catpick--full .cat-tally__lbl { color: var(--warn); }

@media (max-width: 30rem) {
    /* En móvil el contador se pone en fila y ocupa el ancho: apilado deja el
       título con media pantalla vacía al lado. */
    .cat-tally { grid-auto-flow: column; align-items: baseline; gap: var(--space-2); }
}

.catpick__bar {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
}

.catpick__search {
    flex: 1 1 16rem;
    /* La lupa va como fondo y no como otro elemento: un <span> encima del
       input roba el clic justo en el borde izquierdo, que es donde la gente
       apunta para empezar a escribir. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='m20 20-3.5-3.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: left 0.75rem center;
    background-size: 1.05rem;
    padding-inline-start: 2.4rem;
}

.catpick__tools { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.catpick__tool {
    font: inherit;
    font-size: var(--step--1);
    padding: 0.3rem 0.75rem;
    border-radius: var(--radius-full);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-muted);
    cursor: pointer;
    text-decoration: none;
    transition: border-color var(--speed) var(--ease), color var(--speed) var(--ease);
}

.catpick__tool:hover { border-color: var(--border-strong); color: var(--text); }
.catpick__tool--on { border-color: var(--brand-1); color: var(--brand-1); }

/* «Desmarcar todas» deshace trabajo, y el color lo dice antes de pulsarlo. Al
   lado de un filtro con la misma cara, se leía como otro filtro más — que es
   exactamente cómo alguien perdió sus categorías creyendo que limpiaba el
   buscador. */
.catpick__tool--danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 35%, transparent); }
.catpick__tool--danger:hover { border-color: var(--bad); color: var(--bad); }

/* La oferta de deshacer. Ocupa su propia línea y no un rincón del pie: aparece
   justo después de que la parrilla se vació, que es cuando el ojo está ahí. */
.catpick__undo {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    border: 1px solid color-mix(in srgb, var(--brand-1) 35%, transparent);
    background: color-mix(in srgb, var(--brand-1) 8%, transparent);
    font-size: var(--step--1);
    color: var(--text);
}
.catpick__tool--buy { margin-inline-start: auto; color: var(--brand-1); border-color: var(--brand-1); }

.catpick__empty {
    font-size: var(--step--1);
    color: var(--text-muted);
    padding: var(--space-4);
    text-align: center;
}

.catgroup {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    overflow: hidden;
}

.catgroup__head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3);
    cursor: pointer;
    font-size: var(--step--1);
    font-weight: 620;
    list-style: none;
}

.catgroup__head::-webkit-details-marker { display: none; }

/* El triángulo se dibuja acá para que gire: el marcador nativo de <details>
   no se puede animar y en Safari ni siquiera se puede recolorear. */
.catgroup__head::before {
    content: "";
    width: 0.5rem;
    height: 0.5rem;
    border-right: 2px solid var(--text-faint);
    border-bottom: 2px solid var(--text-faint);
    transform: rotate(-45deg);
    transition: transform var(--speed) var(--ease);
    flex-shrink: 0;
}

.catgroup[open] > .catgroup__head::before { transform: rotate(45deg); }
.catgroup__head:hover { background: var(--surface-2); }
.catgroup__icon { font-size: 1.1em; }
.catgroup__name { flex: 1; }

/* Cuántas lleva marcadas esta rama. Vacío cuando son cero: un "0" en cada
   uno de los 16 títulos es ruido que hay que leer para descartar. */
.catgroup__n {
    min-width: 1.4rem;
    text-align: center;
    font-size: var(--step--1);
    color: var(--brand-1);
    font-weight: 700;
}

.catgroup__body {
    display: grid;
    gap: var(--space-2);
    padding: 0 var(--space-3) var(--space-3);
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 210px), 1fr));
}

.catopt {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}

.catopt:hover { border-color: var(--border-strong); background: var(--surface-2); }
.catopt input { width: auto; margin-top: 0.15rem; accent-color: var(--brand-1); flex-shrink: 0; }
.catopt:has(input:checked) { border-color: var(--brand-1); background: var(--gradient-soft); }

.catopt__text { font-size: var(--step--1); line-height: 1.35; }
.catopt__text strong { display: block; font-weight: 600; }
.catopt__text span { color: var(--text-faint); font-size: 0.85em; }

/* Apagada por haber llegado al tope. Se atenúa pero NO se esconde: que
   desaparezca la mitad de la lista al marcar la última haría pensar que se
   rompió algo. */
.catopt--off { opacity: 0.45; cursor: not-allowed; }
.catopt--off:hover { border-color: var(--border); background: transparent; }

/* ==========================================================================
   Contacto — /es/contactanos y /en/contact-us
   --------------------------------------------------------------------------
   Un formulario NO se lee al ancho de un artículo. La primera versión usaba
   `.news-shell` (1080 px) y el resultado eran campos de mil píxeles de largo
   para escribir un nombre, un botón que quedaba solo en una esquina a metros
   del último campo, y un textarea del tamaño de media pantalla.

   El ancho de acá sale de para qué sirve cada cosa: 42rem es donde una línea
   de texto escrita a mano se sigue leyendo cómoda y donde el botón queda a la
   vista del campo que lo precede. Más ancho no cabe más información — solo
   separa las partes que hay que mirar juntas.

   Se pinta dentro de los DOS shells (landing con el modo coming soon, main con
   el sitio abierto), así que centra por su cuenta y no depende de ninguno.
   ========================================================================== */

.contact {
    width: min(100% - 2rem, 42rem);
    margin-inline: auto;
    padding-block: var(--space-7) var(--space-8);
}

.contact__head { margin-bottom: var(--space-5); }

.contact__title {
    font-size: var(--step-3);
    letter-spacing: -0.03em;
    line-height: 1.05;
    margin: 0;
}

.contact__lede {
    margin: var(--space-3) 0 0;
    color: var(--text-muted);
    font-size: var(--step-0);
    /* Más corta que el formulario a propósito: es texto corrido, y el ojo se
       pierde antes en prosa que en una lista de campos etiquetados. */
    max-width: 54ch;
    line-height: 1.55;
}

/* El último campo no lleva el margen de `.field`: lo pone `.contact__actions`,
   que necesita medir esa separación por su cuenta para que la línea del botón
   quede pegada al formulario y no flotando. */
.contact__form .field:last-of-type { margin-bottom: 0; }

.contact__message {
    /* Alto de partida para unas cinco líneas. Antes eran `rows="8"` sobre un
       campo de 1046 px: un hueco enorme que hacía parecer que hay que escribir
       un ensayo. Crece sola con el contenido donde el navegador lo soporta. */
    min-height: 9.5rem;
    /* Solo vertical: horizontal se sale de la tarjeta y rompe la página. */
    resize: vertical;
    field-sizing: content;
    max-height: 26rem;
}

/* --------------------------------------------------------------- el cierre */

.contact__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4);
    margin-top: var(--space-5);
    padding-top: var(--space-5);
    border-top: 1px solid var(--border);
}

/* La letra pequeña AL LADO del botón y no debajo: debajo eran dos párrafos
   sueltos que parecían restos de otra sección, y empujaban el botón lejos del
   formulario. Al lado ocupan el espacio vacío que dejaba el botón. */
/* El que reparte el espacio es el ENVOLTORIO, no cada parrafo: los parrafos
   son hijos suyos, no del flex, y poner el `flex` en ellos dejaba al div con
   su ancho por defecto — que con este texto no cabia al lado del boton y lo
   mandaba a la linea siguiente, que es justo lo que se venia a arreglar. */
.contact__note { flex: 1 1 17rem; min-width: 0; }

.contact__fine {
    margin: 0;
    font-size: var(--step--1);
    line-height: 1.45;
    color: var(--text-muted);
    text-wrap: pretty;
}

.contact__fine + .contact__fine { margin-top: var(--space-2); }

.contact__fine a {
    color: inherit;
    text-decoration: underline;
}

/* El distintivo flotante de reCAPTCHA se esconde porque la atribución ya va
   escrita en `.contact__fine`, que es la alternativa que Google acepta. Se
   esconde con `visibility` y no con `display:none`: el script necesita que el
   elemento siga teniendo caja, y con `display:none` v3 deja de resolver. */
.grecaptcha-badge { visibility: hidden; }

@media (max-width: 32rem) {
    /* En un teléfono el botón a media línea se toca mal. Ocupa el ancho, y la
       letra pequeña baja debajo en vez de pelear por el sitio. */
    .contact__actions { flex-direction: column; align-items: stretch; }
    .contact__submit { width: 100%; }

    /* Y el `flex-basis` de la nota SE ANULA. En columna, `flex: 1 1 17rem` deja
       de pedir 17rem de ancho y pide 17rem de ALTO: la tarjeta salía con 272 px
       de aire debajo de la letra pequeña, un hueco que no se explica mirando la
       página y que sólo aparece en pantallas angostas. */
    .contact__note { flex: 0 1 auto; }
}

/* ------------------------------------------------------------ campo inválido

   Un error de formulario tiene que verse EN el campo que falló, no solo en un
   aviso arriba. Con el aviso a secas, quien manda cinco campos y se equivoca en
   uno tiene que releerlos todos para encontrarlo — y en un formulario largo, la
   mayoría abandona antes.

   El color NO es lo único que marca el error: lleva además el texto debajo y
   `aria-invalid` en el control. Un borde rojo a secas no existe para quien no
   distingue el rojo ni para quien usa lector de pantalla. */

.field.is-invalid > input,
.field.is-invalid > select,
.field.is-invalid > textarea {
    border-color: var(--bad);
    /* El halo repite el aviso en una segunda señal, por si el borde de 1px se
       pierde en una pantalla con poco contraste. */
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad) 18%, transparent);
}

.field__help--error {
    color: var(--bad);
    font-weight: 500;
    display: flex;
    gap: var(--space-2);
    align-items: baseline;
}

/* El aspa del mensaje de campo. Va como ::before y no en el HTML para que no
   la lea el lector de pantalla: ahí el error ya lo anuncia `aria-invalid`, y
   oír "aspa" antes de cada mensaje es ruido. */
.field__help--error::before {
    content: "✕";
    font-size: 0.85em;
    flex: 0 0 auto;
}

/* El aviso del formulario, el que NO se cierra solo. `partials/flash` es el
   único que pinta avisos con `data-flash`; este vive dentro de la página
   porque `layouts/landing` —el del modo coming soon— no incluye ese partial, y
   sin esto el formulario en producción no diría absolutamente nada al enviarse. */
.form-alert { margin-bottom: var(--space-5); }

/* ------------------------------------------------------- contacto: enviado

   El exito REEMPLAZA al formulario en vez de apilarse encima. Una linea verde
   sobre un formulario vacio se lee mal: los campos en blanco parecen estar
   pidiendo que se llenen otra vez, y la duda de "se mando o no" es justo la
   que hace que alguien lo envie dos veces. */

.contact__done { text-align: center; }

.contact__done-mark {
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    /* El dibujo ocupa poco más de la mitad del disco: lleno de borde a borde
       parece un icono recortado, no una marca dentro de un círculo. */
    font-size: 1.5rem;
    margin: var(--space-2) auto var(--space-4);
    border-radius: var(--radius-full);
    font-size: var(--step-1);
    color: var(--ok);
    background: color-mix(in srgb, var(--ok) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--ok) 35%, transparent);
}

.contact__done-title { font-size: var(--step-2); margin: 0 0 var(--space-2); }

.contact__done-body {
    margin: 0 auto var(--space-5);
    max-width: 40ch;
    color: var(--text-muted);
    line-height: 1.55;
    /* El correo se rompe por donde haga falta: uno largo en un telefono sacaba
       la tarjeta de la pantalla. */
    overflow-wrap: anywhere;
}

/* ------------------------------------------------ el pie: objetivos tactiles

   Los enlaces del pie median 20 px de alto con 8 px de hueco entre ellos: por
   debajo del minimo de 24 px de la WCAG 2.2 (AA), que es donde se empieza a
   fallar el toque con el pulgar en movimiento — y en un telefono el pie es
   justo donde se toca a ciegas.

   El hueco PASA a ser padding en vez de sumarse: 20+8 de hueco y 28 con el
   padding dentro dan la misma distancia de un enlace al siguiente, asi que el
   pie se ve exactamente igual y lo unico que cambia es cuanto se puede fallar
   al apuntar. Agrandar sin quitar el hueco habria estirado el pie entero. */

.ftr__col { gap: 0; }

.ftr__col a {
    display: block;
    padding-block: var(--space-1);
}

/* `.legal-links a` SIN el prefijo de la columna, y eso importa: el modo coming
   soon pinta un pie COMPACTO donde esas legales viven sueltas, fuera de
   `.ftr__col--legal`. Con la regla acotada a la columna, el pie completo quedo
   arreglado en local y produccion —que es la que muestra el compacto— siguio
   con enlaces de 20 px. El fallo solo aparecia en el sitio publicado. */
.legal-links a { padding-block: var(--space-1); }

/* Solo el hueco de FILA: el de columna separa las legales cuando van en linea,
   y ponerlo a cero las pegaria unas a otras. */
.legal-links { row-gap: 0; }

/* ------------------------------------------------------- planes: la nota al pie

   Iba con `.center .muted .small` a secas, o sea a todo el ancho del
   contenedor: 179ch en un portátil y 114 en tableta, medido. Una línea de ese
   largo se lee mal porque el ojo pierde el renglón al saltar de vuelta — es la
   misma razón por la que `.legal` se corta en 68ch.

   Clase propia y no un `max-width` sobre `.center`: esas tres son utilidades
   que usa medio sitio, y estrecharlas desde aquí encogería cosas que nadie fue
   a tocar. */

.plans-footnote {
    max-width: 62ch;
    margin-inline: auto;
}
