:root{
    --bg:#ffffff;
    --fg:#111111;
    --accent:#ff5a1f;
    --dim:#6b6b66;

    /* Botón hamburguesa neumórfico ("soft UI"): sombras derivadas
       automáticamente de --bg (65% mezcla con negro/blanco) para
       conservar el mismo contraste relativo del diseño original,
       sea cual sea el color de fondo. No tocar a mano. */
    --neu-shadow-dark: color-mix(in srgb, var(--bg) 65%, black);
    --neu-shadow-light: color-mix(in srgb, var(--bg) 65%, white);
    --neu-icon-color: color-mix(in srgb, var(--bg) 40%, black);
    /* Fuerza de la viñeta OSCURA y fuerte (.scene-wrap-fade, la que vira
       hasta el gris casi negro sólido que empalma con el panel de detrás)
       en el borde inferior de la escena. 0 = invisible, 1 = intensidad
       completa.

       OJO: esto es DISTINTO de la mezcla suave permanente de
       .scene-pin::before/::after (el disolvido del propio borde de la
       foto contra var(--bg), que SIEMPRE está activo, en las 3 posiciones,
       tanto arriba como abajo -así nunca se nota un "corte" en el borde de
       la foto-). Esta variable solo gobierna la viñeta fuerte adicional
       que revela, sin corte, lo que hay debajo al llegar al final: se
       mantiene en 0 durante toda la 1ª y 2ª parada (ahí no hace falta,
       porque no hay nada oscuro debajo que revelar) y solo empieza a
       crecer, de forma suave, según currentFrameExact se acerca al ÚLTIMO
       fotograma de la continuación (el de la 3ª posición, tras el 2º
       salto) -justo cuando aparecen el carrusel de cámaras y su título-,
       llegando al 100% de intensidad exactamente en ese último fotograma
       (ver bgT dentro de render(), en el JS). */
    --fade-opacity:0;
  }
  *{box-sizing:border-box;}
  html,body{
    margin:0;
    padding:0;
    background:var(--bg);
    color:var(--fg);
    font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
    width:100%;
    min-width:100%;
    /* FIX franjas/cortes blancos: además de height:100% (que en <html>
       normalmente ya sigue el viewport real), se fuerza también un
       min-height con la misma cadena de fallback que .scene-wrap/
       .after-story-bg -terminando en el valor JS exacto (--vh) y, donde
       el navegador lo soporte, en dvh, el más preciso de todos-. Así, en
       cualquier navegador o combinación de zoom/orientación en la que
       height:100% se quedase corta por el motivo que sea, el propio fondo
       de <html>/<body> (que ya se tiñe en JS del color exacto de la
       escena, ver setPageBgColor) sigue cubriendo el 100% real de la
       pantalla y de cualquier zona de overscroll, sin dejar nunca una
       tira sin pintar. */
    height:100%;
    min-height:100vh;
    min-height:calc(var(--vh, 1vh) * 100);
    min-height:100svh;
    min-height:100dvh;
    /* SCROLL NATIVO REAL (v2): el documento vuelve a ser una página normal
       que hace scroll de verdad. Esto es imprescindible para que Safari en
       iPhone trate la página como "scroll real" y aplique su
       comportamiento nativo de barra inferior translúcida (igual que en
       Apple Maps / App Store): el contenido pasa por debajo de la barra
       sin hueco ni corte. Con position:fixed + overflow:hidden (como
       estaba antes) el documento nunca se mueve de verdad, así que Safari
       nunca activa ese comportamiento -por eso ningún parche de
       padding/safe-area podía arreglarlo-.

       El "salto" animado entre fotos sigue existiendo exactamente igual
       que antes (misma duración fija, mismo easing, mismo ritmo por
       tramo): lo que cambia es que ahora, en vez de mover un
       transform/--pe, mueve el scroll REAL de la página con esa misma
       curva (ver jumpTo/settleSegment más abajo). Así el navegador ve
       scroll genuino en todo momento, tanto durante los saltos como en
       las zonas de scroll libre (antes de la 1ª parada y después de la
       3ª), y aplica su física e interfaz nativas en todos los casos. */
    overscroll-behavior-y: none; /* evita el rebote elástico en los extremos del documento, pero permite scroll normal dentro */
    /* ---------- Bloqueo de zoom (pellizco y doble toque) ----------
       touch-action:pan-y le dice al navegador que, en toda la página,
       el ÚNICO gesto táctil permitido es el scroll vertical: así se
       descarta de raíz tanto el pellizco (pinch-zoom) como el doble
       toque para ampliar, sin tocar el scroll real que necesita el
       resto de la página (ver el bloque de comentarios de más arriba).
       Los elementos hijos que ya declaran su propio touch-action (p.ej.
       el lienzo de fotogramas o los carruseles con arrastre horizontal)
       siguen funcionando igual: el navegador combina el valor de cada
       elemento con el de sus ancestros, así que esta regla general no
       les quita nada de lo que ya tenían restringido o permitido.
       El remate -por si algún navegador ignorase user-scalable=no del
       viewport o touch-action- va en JS, más abajo (ver bloque
       "Bloqueo de zoom" justo antes de </body>). */
    touch-action: pan-y;
  }

  /* ---------- Contenedor de scroll real de las 3 paradas ---------- */
  /* Alto = 3 pantallas (una por parada). Es scroll de VERDAD: al
     desplazarse por aquí, .scene-wrap (sticky) se queda clavada en
     pantalla mientras este contenedor pasa por debajo -el mismo patrón
     que usa Apple en sus páginas de producto con imagen fija y contenido
     scrolleable-. El salto animado entre paradas (ver jumpTo/goNext/
     goPrev en el JS) mueve el scroll REAL con la misma curva de easing y
     duración fija de siempre, en vez de un transform -así Safari trata
     todo el recorrido como scroll genuino y aplica su barra inferior
     nativa en todo momento, también durante el salto animado-. */
  #storyTrack{
    position:relative;
    height:calc(var(--vh, 1vh) * 100 * 3);
    height:300svh;
    height:300dvh;
  }
  .scene-wrap{
    position:sticky;
    top:0;
    height:100vh;
    height:calc(var(--vh, 1vh) * 100);
    height:100svh;
    height:100dvh;
    width:100%;
    z-index:2;
    /* A veces, al desplazar la escena con transform:translateY() (ver
       applyPostEndOffset), aparecía una línea blanca fina justo en la
       costura con el panel gris de detrás (.after-story-bg): el navegador
       redondea la posición del transform a subpíxeles de forma distinta
       en cada frame, y esa escena (opaca) y el panel de detrás (que no se
       mueve) acaban en capas de composición GPU distintas que a veces no
       encajan al pixel exacto. translateZ(0) fuerza a la escena a vivir
       siempre en su propia capa GPU, estable, y backface-visibility
       evita el "flicker" de repintado -es el arreglo estándar para este
       tipo de costura intermitente entre capas animadas-. */
    transform:translateZ(0);
    backface-visibility:hidden;
  }
  .scene-pin{
    position:absolute;
    inset:0;
    height:100%;
    width:100%;
    overflow:hidden;
    display:flex;
    align-items:center;
    justify-content:center;
    background:var(--bg);
  }

  /* Difuminado hacia el fondo (blanco, o el gris hacia el que va virando
     var(--bg) al deslizar más allá del final) en los bordes SUPERIOR e
     INFERIOR de la escena, para que la foto se disuelva suavemente en el
     fondo en vez de cortarse de golpe -en cualquier posición (1ª, 2ª o
     3ª), no solo cerca del final-. IMPORTANTE: el alto va en % (relativo
     al alto real de .scene-pin) y NO en vh, porque vh en móvil no siempre
     coincide con el alto real visible del contenedor sticky; en % siempre
     queda pegado al borde de lo que de verdad se ve en pantalla.

     Es una curva de easing (muchas paradas, concentradas al principio y
     acelerando hacia el final -la misma familia de curva que
     .after-story-bg usa para su propio degradado permanente-), para que
     la mezcla se perciba continua y no como un escalón. color-mix()
     permite aplicar esa misma curva de opacidad sobre var(--bg) aunque su
     valor cambie en tiempo real (de blanco a gris) mientras se desliza.

     NOTA: a diferencia de .scene-wrap-fade (la viñeta fuerte que sí vira
     hasta un gris casi negro sólido, ver más abajo), esta mezcla es
     siempre suave y siempre está activa -no depende de --fade-opacity-:
     por eso nunca se nota un "corte" ni arriba ni abajo, en ninguna de
     las 3 posiciones. Lo único que cambia según la posición es el propio
     valor de var(--bg) (casi blanco en la 1ª y 2ª, virando a gris cámara
     en la 3ª), así que el matiz de la mezcla ya se adapta solo. */
  .scene-pin::before,
  .scene-pin::after{
    content:"";
    position:absolute;
    left:0;
    right:0;
    height:30%;
    pointer-events:none;
    z-index:5;
  }
  .scene-pin::before{
    top:0;
    background:linear-gradient(to top,
      color-mix(in srgb, var(--bg) 0%, transparent) 0%,
      color-mix(in srgb, var(--bg) 1%, transparent) 8%,
      color-mix(in srgb, var(--bg) 3%, transparent) 16%,
      color-mix(in srgb, var(--bg) 6%, transparent) 24%,
      color-mix(in srgb, var(--bg) 11%, transparent) 32%,
      color-mix(in srgb, var(--bg) 18%, transparent) 40%,
      color-mix(in srgb, var(--bg) 27%, transparent) 48%,
      color-mix(in srgb, var(--bg) 38%, transparent) 55%,
      color-mix(in srgb, var(--bg) 50%, transparent) 62%,
      color-mix(in srgb, var(--bg) 63%, transparent) 69%,
      color-mix(in srgb, var(--bg) 75%, transparent) 75%,
      color-mix(in srgb, var(--bg) 85%, transparent) 81%,
      color-mix(in srgb, var(--bg) 92%, transparent) 87%,
      color-mix(in srgb, var(--bg) 97%, transparent) 93%,
      var(--bg) 100%);
  }
  .scene-pin::after{
    bottom:0;
    background:linear-gradient(to bottom,
      color-mix(in srgb, var(--bg) 0%, transparent) 0%,
      color-mix(in srgb, var(--bg) 1%, transparent) 8%,
      color-mix(in srgb, var(--bg) 3%, transparent) 16%,
      color-mix(in srgb, var(--bg) 6%, transparent) 24%,
      color-mix(in srgb, var(--bg) 11%, transparent) 32%,
      color-mix(in srgb, var(--bg) 18%, transparent) 40%,
      color-mix(in srgb, var(--bg) 27%, transparent) 48%,
      color-mix(in srgb, var(--bg) 38%, transparent) 55%,
      color-mix(in srgb, var(--bg) 50%, transparent) 62%,
      color-mix(in srgb, var(--bg) 63%, transparent) 69%,
      color-mix(in srgb, var(--bg) 75%, transparent) 75%,
      color-mix(in srgb, var(--bg) 85%, transparent) 81%,
      color-mix(in srgb, var(--bg) 92%, transparent) 87%,
      color-mix(in srgb, var(--bg) 97%, transparent) 93%,
      var(--bg) 100%);
  }

  /* Difuminado hacia el fondo en los bordes IZQUIERDO y DERECHO de la
     escena: mismo motivo y misma curva de easing que .scene-pin::before/
     ::after (arriba/abajo), pero en horizontal. Antes solo existía el
     difuminado vertical, así que en pantallas anchas -donde la foto
     (cuadrada) queda centrada con huecos a los lados por el object-fit:
     contain- se notaba un corte recto justo en el borde izquierdo/derecho
     de la foto. Van como elementos reales (no ::before/::after, que ya
     están usados arriba) dentro de .scene-pin. */
  .scene-pin-fade-left,
  .scene-pin-fade-right{
    content:"";
    position:absolute;
    top:0;
    bottom:0;
    width:15%;
    pointer-events:none;
    z-index:5;
  }
  .scene-pin-fade-left{
    left:0;
    background:linear-gradient(to left,
      color-mix(in srgb, var(--bg) 0%, transparent) 0%,
      color-mix(in srgb, var(--bg) 1%, transparent) 8%,
      color-mix(in srgb, var(--bg) 3%, transparent) 16%,
      color-mix(in srgb, var(--bg) 6%, transparent) 24%,
      color-mix(in srgb, var(--bg) 11%, transparent) 32%,
      color-mix(in srgb, var(--bg) 18%, transparent) 40%,
      color-mix(in srgb, var(--bg) 27%, transparent) 48%,
      color-mix(in srgb, var(--bg) 38%, transparent) 55%,
      color-mix(in srgb, var(--bg) 50%, transparent) 62%,
      color-mix(in srgb, var(--bg) 63%, transparent) 69%,
      color-mix(in srgb, var(--bg) 75%, transparent) 75%,
      color-mix(in srgb, var(--bg) 85%, transparent) 81%,
      color-mix(in srgb, var(--bg) 92%, transparent) 87%,
      color-mix(in srgb, var(--bg) 97%, transparent) 93%,
      var(--bg) 100%);
  }
  .scene-pin-fade-right{
    right:0;
    /* BUGFIX/mejora pedida: el degradado de color solo (igual que
       .scene-pin-fade-left) no basta para disimular del todo el corte en
       este borde -el blanco "real" de la propia foto (su fondo de
       estudio, ligeramente distinto de un blanco puro) nunca coincide
       exactamente con var(--bg), así que un simple cambio de tono seguía
       dejando una costura perceptible-. Se ensancha bastante (del 15%
       al 32% del ancho) y, sobre todo, se le añade un desenfoque real
       (backdrop-filter) sobre la propia foto que hay debajo: eso
       difumina los píxeles del final de la imagen en vez de solo
       taparlos con otro color, así que cualquier diferencia de tono
       queda disuelta de verdad, no solo camuflada. El desenfoque, igual
       que el degradado de color, entra de 0 a su valor máximo de forma
       gradual con mask-image -así tampoco se nota un "escalón" en el
       propio borde del desenfoque-. Se aplica en los 3 saltos por igual,
       porque var(--bg)/el desenfoque no dependen de en qué parada se
       esté, solo de que la foto esté pegada a la izquierda (pantallas
       anchas). */
    backdrop-filter:blur(9px);
    -webkit-backdrop-filter:blur(9px);
    /* Antes el desenfoque entraba con solo 2 tramos (transparente ->
       sólido en la mitad del ancho): se notaba como un "parche" borroso
       con un punto donde empieza a notarse. Ahora usa la misma curva de
       easing (muchos escalones, suaves al principio y acelerando hacia
       el final) que ya usan los difuminados de arriba/abajo/izquierda,
       así el desenfoque va entrando de forma gradual y continua, sin que
       se pueda señalar un punto exacto donde "empieza a difuminarse".
       Junto con un blur más suave (9px en vez de 22px) y la zona más
       ancha (45% en vez de 32%), el efecto deja de leerse como "hay un
       borrón ahí" y pasa a sentirse como que la foto simplemente se
       acaba de forma natural. */
    -webkit-mask-image:linear-gradient(to right,
      transparent 0%,
      rgba(0,0,0,0.01) 8%,
      rgba(0,0,0,0.03) 16%,
      rgba(0,0,0,0.06) 24%,
      rgba(0,0,0,0.11) 32%,
      rgba(0,0,0,0.18) 40%,
      rgba(0,0,0,0.27) 48%,
      rgba(0,0,0,0.38) 55%,
      rgba(0,0,0,0.50) 62%,
      rgba(0,0,0,0.63) 69%,
      rgba(0,0,0,0.75) 75%,
      rgba(0,0,0,0.85) 81%,
      rgba(0,0,0,0.92) 87%,
      rgba(0,0,0,0.97) 93%,
      #000 100%);
    mask-image:linear-gradient(to right,
      transparent 0%,
      rgba(0,0,0,0.01) 8%,
      rgba(0,0,0,0.03) 16%,
      rgba(0,0,0,0.06) 24%,
      rgba(0,0,0,0.11) 32%,
      rgba(0,0,0,0.18) 40%,
      rgba(0,0,0,0.27) 48%,
      rgba(0,0,0,0.38) 55%,
      rgba(0,0,0,0.50) 62%,
      rgba(0,0,0,0.63) 69%,
      rgba(0,0,0,0.75) 75%,
      rgba(0,0,0,0.85) 81%,
      rgba(0,0,0,0.92) 87%,
      rgba(0,0,0,0.97) 93%,
      #000 100%);
    background:linear-gradient(to right,
      color-mix(in srgb, var(--bg) 0%, transparent) 0%,
      color-mix(in srgb, var(--bg) 1%, transparent) 8%,
      color-mix(in srgb, var(--bg) 3%, transparent) 16%,
      color-mix(in srgb, var(--bg) 6%, transparent) 24%,
      color-mix(in srgb, var(--bg) 11%, transparent) 32%,
      color-mix(in srgb, var(--bg) 18%, transparent) 40%,
      color-mix(in srgb, var(--bg) 27%, transparent) 48%,
      color-mix(in srgb, var(--bg) 38%, transparent) 55%,
      color-mix(in srgb, var(--bg) 50%, transparent) 62%,
      color-mix(in srgb, var(--bg) 63%, transparent) 69%,
      color-mix(in srgb, var(--bg) 75%, transparent) 75%,
      color-mix(in srgb, var(--bg) 85%, transparent) 81%,
      color-mix(in srgb, var(--bg) 92%, transparent) 87%,
      color-mix(in srgb, var(--bg) 97%, transparent) 93%,
      var(--bg) 100%);
  }
  .scene-pin-fade-right{ width:45%; }

  /* En móvil apenas hay huecos laterales que disimular (la foto ocupa
     casi todo el ancho), así que se oculta directamente. */
  @media (max-width:600px){
    .scene-pin-fade-left,
    .scene-pin-fade-right{
      display:none;
    }
  }


  .frame-canvas{
    height:100%;
    width:100%;
    object-fit:contain;
    display:block;
  }

  /* ---------- Fondo gris oscuro tras la última parada ----------
     Ocupa toda la pantalla pero vive DETRÁS de .scene-wrap (que es opaco),
     así que en reposo no se ve nada de este panel: solo asoma por el hueco
     que la propia escena va dejando al desplazarse hacia arriba (con
     transform, ver applyPostEndOffset) tras la última parada.

     Es sólido, sin degradado propio: la transición de blanco a oscuro ya
     no vive aquí, sino de forma PERMANENTE en el propio borde inferior de
     la escena (ver la viñeta en .scene-wrap-fade, más abajo), que remata
     exactamente en este mismo gris sólido -por eso encajan sin costura,
     se mire en el momento que se mire-. */
  .after-story-bg{
    position:fixed;
    /* Un pelín más allá del viewport por los cuatro lados (en vez de
       inset:0 exacto): un margen de seguridad invisible -es fondo sólido,
       no se nota la diferencia- para que, si la costura con la escena de
       delante (que sí se desplaza con transform) queda a un subpíxel de
       redondeo distinto, nunca se cuele una línea del fondo real de la
       página por debajo. Ampliado a 6px (antes 2px) para cubrir también
       zoom del navegador y pantallas de muy alta densidad, donde un
       redondeo de subpíxel puede ser algo mayor que en una pantalla normal. */
    inset:-6px;
    height:calc(100vh + 12px);
    height:calc(var(--vh, 1vh) * 100 + 12px);
    height:calc(100svh + 12px);
    height:calc(100dvh + 12px);
    /* IMPORTANTE: z-index positivo (no negativo). Con z-index negativo
       algunos navegadores lo pintan por detrás del propio fondo blanco de
       <body>, y el panel dejaba de verse. Con z-index:1, en cambio, queda
       claramente por debajo de .scene-wrap (z-index:2) y por encima del
       fondo de la página, que es justo el orden que necesitamos. */
    z-index:1;
    background:#ffffff;
    /* Misma capa GPU estable que .scene-wrap (ver ahí el porqué): así la
       costura entre ambas nunca se recompone con un subpíxel de más o de
       menos de un frame a otro. */
    transform:translateZ(0);
    backface-visibility:hidden;
  }

  /* ---------- Viñeta permanente: el propio final del fondo blanco ----------
     Detalle FIJO del diseño -no un efecto que "aparece" al hacer scroll-:
     está siempre ahí, en cada foto, desde la primera vez que se pinta la
     página, exactamente igual que un marco o una viñeta de una revista.
     Es hija de .scene-wrap (sube y baja exactamente con la escena) y
     ocupa siempre su borde inferior: el fondo blanco se queda blanco en
     todo lo demás, y solo en este último tramo -bastante por debajo del
     centro, donde ya no hay contenido/texto- empieza, de forma suave y
     progresiva, a virar hacia gris claro, gris medio y finalmente gris
     oscuro sólido, justo en su borde inferior.

     Es clave que su ÚLTIMA parada sea exactamente el mismo gris sólido
     que .after-story-bg (#1c1c1e): así, cuando el usuario desliza más
     allá de la última foto y la escena descubre el panel oscuro de detrás,
     ambos coinciden a la perfección sin importar cuánto se haya deslizado
     -no hace falta calcular ni sincronizar nada por JavaScript, porque los
     dos únicos colores que se tocan (el remate de esta viñeta y el panel
     de detrás) son, literalmente, el mismo color fijo-.

     La curva de opacidad (muchas paradas, un largo tramo inicial 0%-40%
     totalmente transparente y solo después una curva de aceleración
     suave) es la que hace que, mirando cualquier foto, la mayor parte de
     ella se vea perfectamente blanca y solo el remate final, muy abajo,
     insinúe el oscurecimiento -de forma tan gradual que no se percibe
     ningún borde ni línea, solo una sensación de profundidad. */
  .scene-wrap-fade{
    position:absolute;
    left:0;
    right:0;
    /* 1px de solape hacia abajo (en vez de bottom:0 exacto): su color
       final ya es el mismo gris sólido de .after-story-bg, así que un
       pelín de solape es invisible pero asegura que nunca quede, por
       redondeo de subpíxel, una rendija sin cubrir justo en esa costura. */
    bottom:-1px;
    height:calc(28vh + 1px);
    /* No es una viñeta permanente pintada siempre al 100% en cualquier
       parada -eso oscurecía de más la 1ª y 2ª posición sin necesidad, ahí
       no hay nada oscuro debajo que revelar-. Solo se hace visible cerca
       del último fotograma (ver --fade-opacity en :root y render() en el
       JS), justo cuando de verdad hace falta para la costura con el panel
       oscuro. La mezcla suave permanente del borde de la foto (siempre
       activa, arriba y abajo, en las 3 posiciones) vive aparte, en
       .scene-pin::before/::after, más arriba. */
    opacity:var(--fade-opacity);
    background:linear-gradient(to bottom,
      rgba(255,255,255,0) 0%,
      rgba(255,255,255,0) 40%,
      rgba(255,255,255,0.013) 44%,
      rgba(255,255,255,0.049) 48%,
      rgba(255,255,255,0.104) 52%,
      rgba(255,255,255,0.175) 56%,
      rgba(255,255,255,0.259) 60%,
      rgba(255,255,255,0.352) 64%,
      rgba(255,255,255,0.45) 68%,
      rgba(255,255,255,0.55) 72%,
      rgba(255,255,255,0.648) 76%,
      rgba(255,255,255,0.741) 80%,
      rgba(255,255,255,0.825) 84%,
      rgba(255,255,255,0.896) 88%,
      rgba(255,255,255,0.939) 91%,
      rgba(255,255,255,0.972) 94%,
      rgba(255,255,255,0.987) 96%,
      rgba(255,255,255,0.997) 98%,
      #ffffff 100%);
    pointer-events:none;
    z-index:6;
    /* Sin clip-path, sin opacity dinámica, sin transición: siempre
       pintada al 100%, siempre en el sitio. No hay nada que activar ni
       desactivar por JavaScript -es, literalmente, parte fija del fondo. */
    transform:translateZ(0);
    backface-visibility:hidden;
  }

  .scene-hint{
    position:fixed;
    left:50%;
    bottom:26px;
    transform:translateX(-50%);
    font-size:0.72rem;
    letter-spacing:0.14em;
    text-transform:uppercase;
    color:var(--dim);
    z-index:20;
    opacity:0;
    transition:opacity .4s ease;
    pointer-events:none;
  }
  .scene-hint.visible{ opacity:1; }

  /* ---------- Panel de Ajustes "Antes/Después" (subida de fotos): dos
     ranuras ----------
     Los dos recuadros cuadrados de subida (sin editar / editada) usan el
     mismo patrón que otras miniaturas de Ajustes: por defecto invitan a
     subir, y una vez hay foto se convierten en la miniatura con un aviso
     de "Cambiar foto" al pasar el ratón. Estas mismas clases (.ba-slots,
     .ba-slot, .ba-slot-box) las reutiliza también el editor de parejas
     del comparador de "Mis ediciones" (ver más abajo). */
  .ba-slots{
    display:flex;
    align-items:flex-start;
    gap:14px;
    margin:16px 0 6px;
  }
  .ba-slot{ position:relative; flex:1; max-width:170px; display:flex; justify-content:center; }
  .ba-slot-box{
    position:relative;
    width:100%;
    aspect-ratio:3/4;
    border:1.5px dashed rgba(17,17,17,0.32);
    border-radius:10px;
    background:rgba(0,0,0,0.02);
    display:flex;
    align-items:center;
    justify-content:center;
    flex-direction:column;
    gap:6px;
    cursor:pointer;
    overflow:hidden;
    transition:border-color .2s ease;
  }
  .ba-slot-box:hover{ border-color:var(--accent); }
  .ba-slot-box svg{ width:22px; height:22px; color:var(--dim); flex:none; }
  .ba-slot-box span{
    font-size:0.62rem;
    letter-spacing:0.05em;
    text-transform:uppercase;
    color:var(--dim);
    text-align:center;
    padding:0 8px;
  }
  .ba-slot-box:hover svg, .ba-slot-box:hover span{ color:var(--accent); }
  /* Con foto: el recuadro deja de ser un cuadrado 3/4 fijo que recorta la
     imagen (object-fit:cover) y pasa a ajustarse a la forma real de la
     foto -vertical, horizontal o cuadrada-, con un tamaño MÁXIMO (ancho:
     el de la columna, 170px; alto: 230px) que nunca se supera, pero sin
     forzar la imagen a rellenar ese máximo si es más pequeña o de otra
     proporción: manda la imagen, el recuadro solo pone el límite. */
  .ba-slot-box.has-image{
    border-style:solid;
    border-color:rgba(17,17,17,0.14);
    background:#000;
    width:auto;
    max-width:100%;
    aspect-ratio:auto;
    height:auto;
    max-height:230px;
  }
  .ba-slot-box.has-image img{
    display:block;
    width:auto;
    height:auto;
    max-width:100%;
    max-height:230px;
    object-fit:contain;
  }
  .ba-slot-box.is-uploading{ cursor:default; }

  /* ---------- Puntos de progreso ----------
     Indican en qué parada del recorrido está el usuario mientras desliza,
     como los puntos de un carrusel, pero conectados por una raya vertical
     (el "track") para que se lean como un único recorrido y no como
     puntos sueltos. Fijos en el borde derecho, centrados en vertical,
     fuera del área de la persona y del carrusel de cámaras. Solo visibles
     en la vista "Resumen" (igual que .scene-hint, ver goToView).

     Son 4 paradas, no 3: las 3 del recorrido de fotos (WAYPOINTS) más una
     última que representa el tramo negro/gris final (.after-story-bg),
     que es un apartado propio del recorrido y no una prolongación de la
     última foto.

     El relleno naranja (.scene-progress-line-fill) NO salta de punto en
     punto: crece de forma continua, fotograma a fotograma, seguiiendo
     exactamente el mismo progreso que se ve en pantalla (tanto durante el
     salto animado entre paradas como durante el arrastre en tiempo real
     del tramo final) -ver updateProgressTrack en el JS-, así el indicador
     "acompaña" el gesto en vez de limitarse a marcar destinos. */
  .scene-progress{
    /* Eliminado a petición: ya no se muestra el indicador de progreso
       (raya vertical + puntos) en el borde derecho. Se deja "display:none"
       fijo aquí para que ningún cambio de vista en el JS pueda volver a
       mostrarlo. */
    display:none !important;
  }
  .scene-progress-track{
    position:relative;
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:20px;
  }
  .scene-progress-line,
  .scene-progress-line-fill{
    position:absolute;
    left:50%;
    width:2px;
    transform:translateX(-50%);
    border-radius:1px;
  }
  .scene-progress-line{
    background:var(--dim);
    opacity:0.28;
  }
  .scene-progress-line-fill{
    top:0;
    height:0;
    background:var(--accent);
    opacity:0.95;
  }
  .scene-progress .dot{
    position:relative;
    z-index:1;
    width:6px;
    height:6px;
    border-radius:50%;
    background:var(--dim);
    opacity:0.35;
    transform:scale(1);
    transition:opacity .35s ease, transform .35s ease, background-color .35s ease;
  }
  .scene-progress .dot.passed{
    background:var(--accent);
    opacity:0.6;
  }
  .scene-progress .dot.active{
    background:var(--accent);
    opacity:1;
    transform:scale(1.35);
  }

  /* ---------- Continuación en blanco tras la última parada ----------
     Al llegar al final del recorrido, en vez de quedar bloqueado, un
     gesto más hacia adelante revela esta capa blanca deslizándose desde
     abajo hasta cubrir toda la pantalla, dando la sensación de que la
     historia "sigue avanzando" hacia un fondo en blanco. Un gesto hacia
     atrás desde aquí la vuelve a ocultar y regresa a la última imagen. */
  .white-end{
    position:fixed;
    inset:0;
    background:#ffffff;
    z-index:40;
    transform:translateY(100%);
    will-change:transform;
  }

  .scene-title{
    /* Pegado a la pantalla (fixed): ya no se desplaza con nada. Aparece y
       desaparece únicamente cambiando de opacidad, en sincronía con el
       salto de imagen, nunca deslizándose ni subiendo. */
    position:fixed;
    left:3vw;
    top:7vw;
    /* Se agranda al máximo posible según el dispositivo, usando tanto el
       ancho (vw) como el alto (vh) de la pantalla real -manda el más
       restrictivo-, así en móviles muy altos y estrechos, o en landscape
       muy bajo y ancho, siempre cabe en la esquina sin desbordar ni tapar
       a la persona del vídeo, que queda centrada/abajo. */
    width:min(48vw, 32vh, 400px);
    z-index:20;
    opacity:1;
    pointer-events:none;
    background:transparent;
  }
  .scene-title img{
    display:block;
    width:100%;
    height:auto;
    background:transparent;
  }
  /* Mini-texto de presentación debajo del título. Va dentro del mismo
     contenedor que la imagen del título, así que se mueve, aparece y
     desaparece exactamente junto con él (arriba, sin tocar nunca a la
     persona, que queda centrada/abajo). Alineado a la izquierda por
     defecto porque en móvil el título está a la izquierda.
     Tipografía: serif elegante de aire místico/de cuento (estilo
     "Bastelier"), en cursiva para reforzar ese tono narrativo/atemporal.
     El tamaño usa clamp() -crece y encoge de forma fluida con la pantalla,
     nunca a saltos- y max-width en ch (no en vw/px), así el bloque de
     texto siempre cabe dentro del ancho reservado al título y jamás llega
     a superponerse con la persona, sea cual sea el tamaño de pantalla. */
  /* Texto de presentación "Soy fotógrafo...". Antes vivía pegado al
     título (arriba), así que se movía y desaparecía con el scroll junto
     con él. Ahora es un elemento independiente, hijo de .scene-pin (la
     escena fijada con scroll), así que se queda siempre a la altura de
     la cintura de la persona y NUNCA se mueve ni desaparece mientras
     dura la escena. Se ancla por la derecha y su ancho se define en vw
     (no en ch ni px fijos), así se estrecha en proporción directa al
     ancho real de pantalla -clave para calibrar con precisión el punto
     exacto donde termina y que jamás llegue a tocar el cuerpo ni la
     cámara, sea cual sea el dispositivo. */
  /* CAMBIO: el texto vivía anclado a la DERECHA (right:4vw), justo donde
     cae el brazo que sujeta la cámara -> siempre acababa tocándolo o
     montado encima. Lo movemos a la IZQUIERDA, abajo, a la zona blanca
     vacía que queda entre la pierna/cadera y el borde izquierdo de la
     foto (la zona que marcaste en rojo como "correcta"). Ese hueco es
     fondo real en todos los frames de la secuencia, así el texto nunca
     invade cuerpo, brazo ni cámara. */
  /* Móviles pequeños: título compacto en altura (vh) para no invadir hacia
     el centro donde suele estar la cara/cuerpo de la persona. */
  @media (max-width:480px){
    .scene-title{
      left:3vw;
      top:6vw;
      width:min(56vw, 26vh, 300px);
    }
  }
  /* Tablets tipo iPad en adelante: la imagen del cuerpo se pega a la
     izquierda, así que el título se mueve a la derecha para no taparla.
     A partir de aquí usamos un único cálculo continuo (sin más saltos de
     breakpoint) que combina % de ancho y % de alto reales, así crece de
     forma proporcional tanto en iPad como en portátil o monitor grande,
     en vez de quedarse "a medias" en tamaños intermedios como una tablet. */
  @media (min-width:768px){
    .scene-title{
      left:auto;
      right:3vw;
      top:7vw;
      width:min(50vw, 50vh, 780px);
    }
  }
  /* Pantallas grandes (portátiles/monitores anchos): al haber mucho más
     ancho que alto disponible, dejamos que el alto (vh) sea quien más
     mande, para que siga creciendo con pantallas más altas sin volverse
     desproporcionadamente ancha. */
  @media (min-width:1200px){
    .scene-title{
      width:min(34vw, 55vh, 900px);
    }
  }

  .loader{
    position:fixed;
    inset:0;
    background:var(--bg);
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    z-index:100;
    transition:opacity .5s ease;
  }
  .loader-bar{
    width:160px;
    height:2px;
    background:rgba(0,0,0,0.12);
    margin-top:18px;
    overflow:hidden;
    border-radius:2px;
  }
  .loader-fill{
    height:100%;
    width:0%;
    background:var(--accent);
    transition:width .15s ease-out;
  }
  .loader-label{
    font-size:0.72rem;
    letter-spacing:0.14em;
    text-transform:uppercase;
    color:var(--dim);
  }
  /* Spinner de "cargando" bajo el logo/animación de entrada. Oculto por
     defecto: solo se enseña (.is-visible, activada por JS) si al acabar
     la animación de entrada todavía faltan fotos de la nube por llegar,
     ver waitForSiteAssets() en scroll-engine.js. */
  .loader-spinner{
    width:26px;
    height:26px;
    margin-top:22px;
    border-radius:50%;
    border:2.5px solid rgba(0,0,0,0.12);
    border-top-color:var(--accent);
    opacity:0;
    pointer-events:none;
    transition:opacity .35s ease;
  }
  .loader-spinner.is-visible{
    opacity:1;
    animation:loaderSpinnerSpin 0.85s linear infinite;
  }
  @keyframes loaderSpinnerSpin{
    to{ transform:rotate(360deg); }
  }

  /* ---------- Vídeo de preloader (animación de inicio) ---------- */
  /* Contenedor que fija el tamaño (igual que antes tenía el <video>) y
     sobre el que se apoya el difuminado de los bordes: al <video> ya no
     le hace falta más que rellenar este cuadro. */
  .loader-video-wrap{
    position:relative;
    width:min(58vw, 58vh, 380px);
    aspect-ratio:1/1;
    display:block;
    /* Entrada tipo Netflix: el vídeo nunca "aparece" de golpe, siempre
       se funde suavemente en cuanto empieza a reproducirse de verdad
       (ver clase .is-playing, activada por JS en el evento 'playing').
       Así, si el arranque tarda una fracción de segundo, el usuario ve
       un fundido limpio en vez de un primer fotograma congelado. */
    opacity:0;
    transition:opacity .45s ease;
  }
  .loader-video-wrap.is-playing{ opacity:1; }
  .loader-video{
    width:100%;
    height:100%;
    display:block;
    object-fit:contain;
    /* Nunca debe poder tocarse/pulsarse: así ningún gesto accidental
       puede activar los controles nativos del navegador (el "botón"
       de silenciar/reanudar con icono de onda que algunos navegadores
       superponen a los vídeos) durante la animación de entrada. */
    pointer-events:none;
  }
  /* Capa por encima del vídeo que funde sus CUATRO bordes (arriba, abajo,
     izquierda y derecha) hacia var(--bg) -el mismo blanco/gris de fondo de
     la escena en cada momento-, para que el vídeo no se vea nunca como un
     cuadrado con un corte recto, sino que se disuelva de forma suave por
     los cuatro lados.

     Truco: se superponen dos degradados lineales -uno horizontal y otro
     vertical-, cada uno opaco (color var(--bg)) en su par de bordes y
     totalmente transparente en el centro. Al pintarse uno encima del otro,
     donde el de arriba es transparente se ve el de abajo a través suyo:
     así los cuatro bordes quedan cubiertos por igual (incluidas las
     esquinas, donde ambos se solapan) y solo el centro del vídeo queda
     limpio, sin que se note nunca la silueta cuadrada del clip. */
  .loader-video-wrap::after{
    content:'';
    position:absolute;
    inset:0;
    pointer-events:none;
    /* Se añade un degradado RADIAL (centrado, círculo) por debajo de los
       dos lineales de siempre: el marco/recuadro de la foto que hay dentro
       del vídeo está ligeramente rotado, así que sus bordes caen en
       diagonal y un difuminado solo horizontal+vertical deja esas esquinas
       demasiado nítidas. El radial cubre justo eso, disolviendo por igual
       en todas direcciones. También se ensancha la zona de transición de
       los lineales (antes 22%→78%, ahora 28%→72%) para que el fundido sea
       más largo y gradual y dej de leerse como un "marco" recto. */
    background:
      radial-gradient(circle at 50% 50%, transparent 0%, transparent 36%, var(--bg) 80%),
      linear-gradient(to right, var(--bg) 0%, transparent 28%, transparent 72%, var(--bg) 100%),
      linear-gradient(to bottom, var(--bg) 0%, transparent 28%, transparent 72%, var(--bg) 100%);
  }

  /* ---------- Correo e Instagram (esquina inferior izquierda) ----------
     Igual que .proyectos-header: opacidad a 0 por defecto y controlada
     fotograma a fotograma desde JS (updateSocialFade en scroll-engine.js)
     mientras se arrastra entre paradas, para que el fundido vaya al mismo
     ritmo exacto del gesto. La clase "visible" es el estado ya asentado
     (parada 1, stepIndex === 0) y sirve de respaldo si por lo que sea no
     hay ningún JS corriendo. SIN transition en opacity por el mismo
     motivo que .proyectos-header: el propio JS ya recalcula la curva. */
  .social-icons{
    position:fixed;
    bottom:max(22px, env(safe-area-inset-bottom));
    left:22px;
    z-index:200;
    display:flex;
    gap:10px;
    opacity:0;
    pointer-events:none;
  }
  .social-icons.visible{ opacity:1; pointer-events:auto; }
  .social-icon{
    width:38px;
    height:38px;
    border-radius:50%;
    background:rgba(255,255,255,0.55);
    backdrop-filter:blur(6px);
    -webkit-backdrop-filter:blur(6px);
    display:flex;
    align-items:center;
    justify-content:center;
    color:var(--fg);
    transition:background .2s ease, transform .2s ease;
  }
  .social-icon:hover{ background:rgba(255,255,255,0.8); transform:translateY(-1px); }

  .menu-toggle{
    position:fixed;
    top:22px;
    right:22px;
    z-index:200;
    width:44px;
    height:44px;
    border:none;
    background:none;
    border-radius:50%;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    gap:5px;
    cursor:pointer;
    padding:0;
    opacity:1;
    -webkit-tap-highlight-color:transparent;
    -webkit-touch-callout:none;
    user-select:none;
    transition:opacity .35s ease;
  }
  /* Cáscara neumórfica: círculo con sombra doble (clara/oscura) que da
     efecto de relieve sobre --bg, y se "hunde" (sombra invertida hacia
     dentro) al pulsar. Tamaños escalados desde el diseño original de
     150px a los 44px reales del botón, manteniendo la misma proporción. */
  .menu-toggle::before{
    position:absolute;
    inset:0;
    display:block;
    content:" ";
    border-radius:50%;
    filter:blur(2px);
    transform:scale(1);
    transition:box-shadow .3s ease-out, transform .3s ease-out;
    box-shadow:
      3px 3px 6px var(--neu-shadow-dark),
      -3px -3px 6px var(--neu-shadow-light),
      inset -3px -3px 6px transparent,
      inset 3px 3px 6px transparent;
  }
  .menu-toggle.open::before{
    transition:box-shadow .3s ease-in, transform .3s ease-in;
    transform:scale(1.05);
    box-shadow:
      3px 3px 6px var(--neu-shadow-dark),
      -3px -3px 6px var(--neu-shadow-light),
      inset -3px -3px 6px var(--neu-shadow-light),
      inset 3px 3px 6px var(--neu-shadow-dark);
  }
  /* Mientras se está reproduciendo la intro de fotogramas (todavía no se
     ha llegado a la última parada del recorrido, ver stepIndex en
     scroll-engine.js) el botón de menú se oculta: no tiene sentido poder
     abrir el menú a mitad de la intro. En cuanto se llega a esa última
     parada (ya "dentro" de la web) vuelve a aparecer. */
  .menu-toggle.intro-hidden{
    opacity:0;
    pointer-events:none;
  }
  .menu-toggle span{
    position:relative;
    display:block;
    width:20px;
    height:2px;
    background:var(--neu-icon-color);
    border-radius:2px;
    transition:transform .3s ease, opacity .3s ease, top .3s ease;
  }
  .menu-toggle.open span{
    transition:transform .3s ease-in, opacity .3s ease-in, top .3s ease-in;
  }
  .menu-toggle.open span:nth-child(1){ transform:translateY(7px) rotate(45deg); }
  .menu-toggle.open span:nth-child(2){ opacity:0; }
  .menu-toggle.open span:nth-child(3){ transform:translateY(-7px) rotate(-45deg); }

  .menu-panel{
    position:fixed;
    top:74px;
    right:22px;
    z-index:199;
    background:#ffffff;
    border-radius:14px;
    box-shadow:0 12px 40px rgba(0,0,0,0.12);
    padding:10px;
    min-width:180px;
    opacity:0;
    transform:translateY(-8px) scale(0.98);
    pointer-events:none;
    transition:opacity .25s ease, transform .25s ease;
  }
  .menu-panel.open{
    opacity:1;
    transform:translateY(0) scale(1);
    pointer-events:auto;
  }
  .menu-item{
    display:flex;
    align-items:center;
    justify-content:space-between;
    width:100%;
    background:transparent;
    border:none;
    text-align:left;
    font-family:'Fraunces','Georgia',serif;
    font-size:0.98rem;
    color:var(--fg);
    padding:11px 14px;
    border-radius:9px;
    cursor:pointer;
    transition:background .2s ease, color .2s ease;
  }
  .menu-item-label{
    display:flex;
    align-items:center;
    gap:10px;
  }
  .menu-item-icon{
    flex:none;
    width:18px;
    height:18px;
    opacity:0.7;
  }
  /* "Ajustes" es un panel de administración (sube fotos, etc.), no algo
     pensado para cualquier visitante. Se queda oculto por defecto y solo
     se muestra en el dispositivo del admin (ver el script de admin). */
  .menu-item[data-view="ajustes"]{ display:none; }
  .menu-item:hover{ background:rgba(0,0,0,0.045); }
  .menu-item.active{
    color:var(--accent);
    background:rgba(255,90,31,0.08);
    font-style:italic;
  }
  .menu-item .dot{
    width:6px;
    height:6px;
    border-radius:50%;
    background:var(--accent);
    opacity:0;
    transition:opacity .2s ease;
  }
  .menu-item.active .dot{ opacity:1; }

  /* Aviso suave: "estamos en esta sección" */
  .menu-toast{
    position:fixed;
    top:74px;
    right:22px;
    z-index:198;
    background:rgba(255,255,255,0.92);
    backdrop-filter:blur(6px);
    -webkit-backdrop-filter:blur(6px);
    color:var(--dim);
    font-family:'Fraunces','Georgia',serif;
    font-style:italic;
    font-size:0.85rem;
    padding:10px 16px;
    border-radius:10px;
    box-shadow:0 8px 24px rgba(0,0,0,0.08);
    opacity:0;
    transform:translateY(-6px);
    pointer-events:none;
    transition:opacity .5s ease, transform .5s ease;
  }
  .menu-toast.visible{
    opacity:1;
    transform:translateY(0);
  }

  /* ---------- Vistas secundarias (Sobre mí / Ediciones) ---------- */
  .subview{
    position:fixed;
    inset:0;
    z-index:150;
    background:var(--bg);
    overflow-y:auto;
    -webkit-overflow-scrolling:touch;
    display:none;
    padding:0 0 80px;
  }
  .subview.active{ display:block; }
  .subview-inner{
    max-width:720px;
    margin:0 auto;
    padding:120px 24px 40px;
  }
  .subview-eyebrow{
    font-size:0.72rem;
    letter-spacing:0.14em;
    text-transform:uppercase;
    color:var(--accent);
    margin:0 0 14px;
  }
  .subview h1{
    font-family:'Fraunces','Georgia',serif;
    font-weight:600;
    font-size:clamp(1.9rem, 5vw, 3rem);
    margin:0 0 22px;
    line-height:1.1;
  }
  /* ---------- Sobre mí: nombre a modo de "entrada" ----------
     Antes llevaba encima la etiqueta "SOBRE MÍ" y un h1 genérico igual
     que cualquier otra vista secundaria. Ahora el nombre solo (sin
     etiqueta) es el propio titular: grande, en cursiva serif como los
     títulos "de escena" del resto del sitio (ver .equipo-title), con su
     misma rayita de acento debajo, y con una pequeña entrada animada
     -sube y se desvanece hacia dentro- cada vez que se abre esta vista,
     en vez de aparecer ya puesto de golpe. */
  .about-name{
    font-family:'Fraunces','Georgia',serif;
    font-weight:700;
    font-style:italic;
    font-size:clamp(2.6rem, 8vw, 4.6rem);
    line-height:1;
    letter-spacing:0.01em;
    margin:4px 0 6px;
    color:var(--fg);
    animation:aboutNameIn .55s cubic-bezier(.16,1,.3,1) both;
  }
  .about-name::after{
    content:'';
    display:block;
    width:56px;
    height:5px;
    margin-top:16px;
    border-radius:2px;
    background:var(--accent);
    animation:aboutNameRuleIn .5s ease .12s both;
  }
  @keyframes aboutNameIn{
    from{ opacity:0; transform:translateY(16px) scale(0.97); }
    to{ opacity:1; transform:translateY(0) scale(1); }
  }
  @keyframes aboutNameRuleIn{
    from{ opacity:0; width:0; }
    to{ opacity:1; width:56px; }
  }
  .subview p{
    font-family:'Fraunces','Georgia',serif;
    font-size:1.05rem;
    line-height:1.7;
    color:var(--fg);
    margin:0 0 18px;
  }
  .subview p.lead{
    font-style:italic;
    color:var(--dim);
    font-size:1.2rem;
  }

  /* ---------- Sobre mí: tira flotante 3D (encima del título) ----------
     Pila pequeña con las mismas fotos del collage de abajo, pensada para
     verse ANTES de que empiece cualquier texto: entra ya "viva" (fundido
     + balanceo continuo por foto) y, además, toda la pila se inclina en
     3D según la posición del ratón/dedo dentro del bloque (ver JS,
     attachMiniFloatParallax). Oculta por completo si no hay fotos
     propias subidas -no deja hueco reservado-. */
  .about-mini-float{
    display:none;
    position:relative;
    height:280px;
    margin:0 0 10px;
    perspective:1200px;
    overflow:hidden;
    /* Antes el contenedor cortaba las fotos laterales en seco al llegar
       al borde (overflow:hidden hace falta para que no rompan el
       layout ni generen scroll horizontal). Con esta máscara, en vez de
       un corte recto, el contenido se desvanece de forma gradual por
       los 4 lados -izquierda/derecha y arriba/abajo-, así se funde con
       el fondo en vez de chocar contra un rectángulo visible. */
    -webkit-mask-image:
      linear-gradient(to right, transparent 0%, #000 16%, #000 84%, transparent 100%),
      linear-gradient(to bottom, transparent 0%, #000 14%, #000 86%, transparent 100%);
    -webkit-mask-composite:source-in, source-in;
    mask-image:
      linear-gradient(to right, transparent 0%, #000 16%, #000 84%, transparent 100%),
      linear-gradient(to bottom, transparent 0%, #000 14%, #000 86%, transparent 100%);
    mask-composite:intersect;
  }
  .about-mini-float::before{
    /* halo suave detrás del carrusel, con el color de acento, para dar
       algo de "brillo ambiental" sin necesidad de luces 3D reales */
    content:"";
    position:absolute;
    inset:-30px -6% auto -6%;
    height:320px;
    background:radial-gradient(ellipse at center, rgba(255,90,31,0.26), transparent 72%);
    filter:blur(8px);
    pointer-events:none;
    animation:miniFloatGlow 6s ease-in-out infinite;
  }
  .mini-float-stage{
    position:relative;
    width:100%;
    height:100%;
    transform-style:preserve-3d;
    transform:rotateY(var(--tiltY, 0deg)) rotateX(var(--tiltX, 0deg));
    transition:transform .35s ease;
  }
  .mini-float-item{
    position:absolute;
    top:50%;
    left:50%;
    width:220px;
    height:220px;
    transition:transform .8s cubic-bezier(.22,1,.36,1), opacity .8s ease, filter .8s ease;
    cursor:pointer;
  }
  .mini-float-inner{
    position:relative;
    width:100%;
    height:100%;
    border-radius:26px;
    overflow:hidden;
    border:4px solid var(--bg);
    box-shadow:0 30px 55px rgba(0,0,0,0.30), 0 8px 16px rgba(0,0,0,0.18), 0 0 0 1px rgba(0,0,0,0.04);
  }
  .mini-float-item.is-active .mini-float-inner{
    animation:miniFloatPulse 3.2s ease-in-out infinite;
    box-shadow:0 34px 64px rgba(0,0,0,0.34), 0 10px 20px rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.05);
  }
  .mini-float-inner::after{
    /* brillo tipo "cristal" cruzando la esquina superior, para reforzar
       la sensación 3D/glossy incluso con la foto quieta */
    content:"";
    position:absolute;
    inset:0;
    background:linear-gradient(135deg, rgba(255,255,255,0.32) 0%, rgba(255,255,255,0) 28%, rgba(255,255,255,0) 72%, rgba(255,255,255,0.1) 100%);
    pointer-events:none;
  }
  .mini-float-item img{
    width:100%;
    height:100%;
    object-fit:cover;
    display:block;
  }
  @keyframes miniFloatPulse{
    0%, 100%{ transform:scale(1); }
    50%{ transform:scale(1.035); }
  }
  @keyframes miniFloatGlow{
    0%, 100%{ opacity:.7; }
    50%{ opacity:1; }
  }
  @media (max-width:600px){
    .about-mini-float{ height:210px; }
    .mini-float-item{ width:160px; height:160px; }
  }
  @media (prefers-reduced-motion:reduce){
    .mini-float-item.is-active .mini-float-inner{ animation:none; }
    .mini-float-stage{ transition:none; }
    .about-mini-float::before{ animation:none; }
  }

  /* ---------- Sobre mí: bloque de contacto ---------- */
  .contact-block{
    margin-top:28px;
    padding-top:26px;
    border-top:1px solid rgba(0,0,0,0.1);
  }
  .contact-eyebrow{
    font-family:'Fraunces','Georgia',serif;
    font-style:italic;
    font-size:0.82rem;
    letter-spacing:0.04em;
    text-transform:uppercase;
    color:var(--dim);
    margin:0 0 12px;
  }
  .contact-links{
    display:flex;
    flex-wrap:wrap;
    gap:10px;
  }
  .contact-link{
    display:inline-flex;
    align-items:center;
    gap:8px;
    background:#fff;
    border:1px solid rgba(0,0,0,0.14);
    border-radius:999px;
    padding:10px 16px;
    font-family:'Fraunces','Georgia',serif;
    font-size:0.86rem;
    color:var(--fg);
    text-decoration:none;
    transition:border-color .2s ease, color .2s ease, box-shadow .2s ease;
  }
  .contact-link:hover{
    border-color:rgba(255,90,31,0.5);
    color:var(--accent);
    box-shadow:0 2px 10px rgba(255,90,31,0.12);
  }
  .contact-link-icon{ flex:none; width:16px; height:16px; }
  .contact-note{
    margin:12px 0 0;
    font-size:0.82rem;
    font-style:italic;
    color:var(--dim);
  }

  /* ---------- Sobre mí: fotos enteras junto al texto ---------- */
  /* Cada foto se inserta (ver about-collage.js) justo antes de un
     párrafo, ENTERA -sin recortar, a diferencia del mosaico anterior que
     usaba object-fit:cover- y alineada a un lado u otro, alternando.
     Nace en opacity:0 + desplazada hacia abajo; solo gana ".is-visible"
     -que dispara el fundido+deslizamiento hacia su sitio- cuando entra en
     el viewport al hacer scroll (IntersectionObserver, ver JS): así van
     apareciendo poco a poco, una a una, en vez de cargarse todas de
     golpe.

     Dos comportamientos según el ancho de pantalla:
     - Móvil (por debajo de 681px): foto y párrafo son bloques separados,
       la foto solo se alinea a un lado con margin auto, alternando
       izquierda/derecha foto a foto (i par -> derecha, i impar ->
       izquierda). El texto NO rodea la foto.
     - Pantallas grandes (681px o más): la foto flota (float) a un lado,
       alternando igual, y el texto de los párrafos siguientes fluye
       envolviéndola en vez de quedar en su propio bloque debajo. */
  .about-photo{
    display:block;
    width:36%;
    max-width:140px;
    height:auto;
    border-radius:14px;
    box-shadow:0 14px 28px rgba(0,0,0,0.16);
    margin:0 0 18px;
    opacity:0;
    transition:opacity .85s cubic-bezier(.19,1,.22,1), transform .85s cubic-bezier(.19,1,.22,1);
  }
  .about-photo--right{
    margin-left:auto;
    margin-right:0;
    transform:translateY(30px) rotate(1.3deg);
  }
  .about-photo--left{
    margin-left:0;
    margin-right:auto;
    transform:translateY(30px) rotate(-1.3deg);
  }
  .about-photo--right.is-visible{ opacity:1; transform:translateY(0) rotate(1.3deg); }
  .about-photo--left.is-visible{ opacity:1; transform:translateY(0) rotate(-1.3deg); }

  /* Contiene los flotantes de dentro (si no, el bloque de texto podría
     colapsar su altura y desbordar sobre lo que viene después). Solo
     tiene efecto real en pantallas grandes, donde las fotos flotan. */
  .about-body::after{
    content:"";
    display:table;
    clear:both;
  }

  @media (min-width:681px){
    .about-photo{
      width:24%;
      max-width:170px;
      margin:4px 0 16px;
    }
    .about-photo--right{
      float:right;
      margin:4px 0 16px 24px;
    }
    .about-photo--left{
      float:left;
      margin:4px 24px 16px 0;
    }
  }
  @media (prefers-reduced-motion:reduce){
    .about-photo{ transition:opacity .3s ease; }
    .about-photo--right,
    .about-photo--left{ transform:none; }
  }

  /* ---------- Sobre mí: tarjeta de invitación (sin fotos propias aún) ---------- */
  .about-collage{
    margin:6px 0 36px;
  }
  .about-collage-grid{
    display:flex;
    flex-wrap:wrap;
    gap:14px;
  }
  .about-collage-item{
    position:relative;
    flex:0 0 auto;
    width:calc(33.333% - 10px);
    aspect-ratio:3/4;
    border-radius:14px;
    overflow:hidden;
    background:linear-gradient(160deg, #f3f3f1, #e8e8e4);
    box-shadow:0 10px 24px rgba(0,0,0,0.10);
    opacity:0;
    transform:translateY(20px) rotate(var(--tilt, 0deg)) scale(0.96);
    animation:aboutCollageIn .65s cubic-bezier(.19,1,.22,1) forwards,
              aboutCollagePulse 7s ease-in-out infinite;
    transition:transform .32s ease, box-shadow .32s ease;
  }
  .about-collage-item:nth-child(6n+1){ --tilt:-2.2deg; animation-delay:0s, 0s; }
  .about-collage-item:nth-child(6n+2){ --tilt:1.6deg;  animation-delay:.08s, .4s; }
  .about-collage-item:nth-child(6n+3){ --tilt:-1deg;   animation-delay:.16s, .8s; }
  .about-collage-item:nth-child(6n+4){ --tilt:2deg;    animation-delay:.24s, 1.2s; }
  .about-collage-item:nth-child(6n+5){ --tilt:-1.6deg; animation-delay:.32s, 1.6s; }
  .about-collage-item:nth-child(6n+6){ --tilt:1.2deg;  animation-delay:.4s, 2s; }
  .about-collage-item:hover{
    transform:translateY(-3px) rotate(0deg) scale(1.03);
    box-shadow:0 18px 34px rgba(0,0,0,0.16);
    z-index:2;
  }
  .about-collage-item img{
    width:100%;
    height:100%;
    object-fit:cover;
    display:block;
  }
  .about-collage-item .about-collage-dot{
    position:absolute;
    top:10px;
    right:10px;
    width:7px;
    height:7px;
    border-radius:50%;
    background:var(--accent);
    opacity:0;
    transition:opacity .25s ease;
  }
  .about-collage-item:hover .about-collage-dot{ opacity:1; }
  @keyframes aboutCollageIn{
    to{ opacity:1; transform:translateY(0) rotate(var(--tilt, 0deg)) scale(1); }
  }
  @keyframes aboutCollagePulse{
    0%, 100%{ box-shadow:0 10px 24px rgba(0,0,0,0.10); }
    50%{ box-shadow:0 15px 30px rgba(0,0,0,0.15); }
  }
  /* Tarjeta de invitación cuando aún no hay fotos propias subidas */
  .about-collage-item.is-placeholder{
    width:100%;
    aspect-ratio:16/7;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    gap:8px;
    cursor:pointer;
    border:1.5px dashed rgba(0,0,0,0.16);
    background:rgba(0,0,0,0.015);
    animation:aboutCollageIn .65s cubic-bezier(.19,1,.22,1) forwards;
    transition:border-color .25s ease, background .25s ease, transform .3s ease;
  }
  .about-collage-item.is-placeholder:hover{
    border-color:rgba(255,90,31,0.45);
    background:rgba(255,90,31,0.04);
    transform:translateY(-2px);
  }
  .about-collage-item.is-placeholder svg{
    width:22px;
    height:22px;
    color:var(--dim);
    opacity:.6;
  }
  .about-collage-item.is-placeholder span{
    font-family:'Fraunces','Georgia',serif;
    font-style:italic;
    font-size:0.92rem;
    color:var(--dim);
  }
  .about-collage-hint{
    margin-top:14px;
    font-size:0.85rem;
    font-style:italic;
    color:var(--dim);
  }
  .about-collage-hint button{
    background:none;
    border:none;
    padding:0;
    font:inherit;
    font-style:italic;
    color:var(--accent);
    cursor:pointer;
    text-decoration:underline;
    text-underline-offset:3px;
  }
  @media (max-width:600px){
    .about-collage-item{ width:calc(50% - 7px); }
  }

  /* Mosaico editorial por número de fotos: cada cantidad (1 a 5) tiene su
     propio reparto, pensado para que en móvil (donde hay poco ancho) se
     vea igual de cuidado que en escritorio, en vez de depender de la
     cuadrícula genérica de arriba (que con 1 o 3 fotos deja huecos raros
     o una foto suelta). Con 6 fotos la cuadrícula genérica ya reparte
     perfecto (dos filas de tres), así que no se toca. */
  .about-collage-grid--n1,
  .about-collage-grid--n2,
  .about-collage-grid--n3,
  .about-collage-grid--n4,
  .about-collage-grid--n5{
    gap:14px;
  }
  .about-collage-grid--n1 .about-collage-item,
  .about-collage-grid--n2 .about-collage-item,
  .about-collage-grid--n3 .about-collage-item,
  .about-collage-grid--n4 .about-collage-item,
  .about-collage-grid--n5 .about-collage-item{
    width:100%;
    height:100%;
    aspect-ratio:auto;
    --tilt:0deg;
  }

  /* 1 foto: una sola pieza grande y cinematográfica. */
  .about-collage-grid--n1{ display:block; }
  .about-collage-grid--n1 .about-collage-item{ aspect-ratio:16/9; }
  @media (max-width:600px){
    .about-collage-grid--n1 .about-collage-item{ aspect-ratio:4/3.1; }
  }

  /* 2 fotos: dos piezas lado a lado en escritorio; en móvil, apiladas
     para que cada una tenga anchura de sobra. */
  .about-collage-grid--n2{
    display:grid;
    grid-template-columns:1.15fr 1fr;
    grid-template-rows:1fr;
    aspect-ratio:16/8.3;
  }
  @media (max-width:600px){
    .about-collage-grid--n2{
      grid-template-columns:1fr;
      grid-template-rows:1fr 1fr;
      gap:10px;
      aspect-ratio:3/3.6;
    }
  }

  /* 3 fotos: en escritorio, una grande a la izquierda + dos apiladas a
     la derecha (tríptico editorial clásico). En móvil se gira: una
     grande arriba + dos pequeñas abajo, que aprovecha mucho mejor un
     ancho estrecho que tres columnas finas. */
  .about-collage-grid--n3{
    display:grid;
    grid-template-columns:1.5fr 1fr;
    grid-template-rows:1fr 1fr;
    aspect-ratio:16/9.5;
  }
  .about-collage-grid--n3 .about-collage-item:nth-child(1){ grid-area:1 / 1 / 3 / 2; }
  .about-collage-grid--n3 .about-collage-item:nth-child(2){ grid-area:1 / 2 / 2 / 3; }
  .about-collage-grid--n3 .about-collage-item:nth-child(3){ grid-area:2 / 2 / 3 / 3; }
  @media (max-width:600px){
    .about-collage-grid--n3{
      grid-template-columns:1fr 1fr;
      grid-template-rows:auto auto;
      gap:10px;
      aspect-ratio:3/3.3;
    }
    .about-collage-grid--n3 .about-collage-item:nth-child(1){ grid-area:1 / 1 / 2 / 3; }
    .about-collage-grid--n3 .about-collage-item:nth-child(2){ grid-area:2 / 1 / 3 / 2; }
    .about-collage-grid--n3 .about-collage-item:nth-child(3){ grid-area:2 / 2 / 3 / 3; }
  }

  /* Mosaico editorial: se activa solo con 4 o 5 fotos, que son los
     números "incómodos" para una cuadrícula de 3 en 3 (dejarían una foto
     suelta). Con 1, 2, 3 o 6 fotos se mantiene la cuadrícula normal de
     arriba, que ya reparte bien el espacio. */
  .about-collage-grid.about-collage-grid--n4,
  .about-collage-grid.about-collage-grid--n5{
    display:grid;
    grid-template-columns:1.6fr 1fr 1fr;
    grid-template-rows:1fr 1fr;
    aspect-ratio:16/9.2;
    gap:14px;
  }
  .about-collage-grid.about-collage-grid--n4 .about-collage-item,
  .about-collage-grid.about-collage-grid--n5 .about-collage-item{
    width:100%;
    height:100%;
    aspect-ratio:auto;
    --tilt:0deg;
  }
  /* 4 fotos: una grande vertical a la izquierda + dos arriba a la
     derecha + una ancha abajo a la derecha. */
  .about-collage-grid--n4 .about-collage-item:nth-child(1){ grid-area:1 / 1 / 3 / 2; }
  .about-collage-grid--n4 .about-collage-item:nth-child(2){ grid-area:1 / 2 / 2 / 3; }
  .about-collage-grid--n4 .about-collage-item:nth-child(3){ grid-area:1 / 3 / 2 / 4; }
  .about-collage-grid--n4 .about-collage-item:nth-child(4){ grid-area:2 / 2 / 3 / 4; }
  /* 5 fotos: una grande vertical a la izquierda + cuatro pequeñas en
     una cuadrícula 2x2 a la derecha. */
  .about-collage-grid--n5 .about-collage-item:nth-child(1){ grid-area:1 / 1 / 3 / 2; }
  .about-collage-grid--n5 .about-collage-item:nth-child(2){ grid-area:1 / 2 / 2 / 3; }
  .about-collage-grid--n5 .about-collage-item:nth-child(3){ grid-area:1 / 3 / 2 / 4; }
  .about-collage-grid--n5 .about-collage-item:nth-child(4){ grid-area:2 / 2 / 3 / 3; }
  .about-collage-grid--n5 .about-collage-item:nth-child(5){ grid-area:2 / 3 / 3 / 4; }
  @media (max-width:600px){
    .about-collage-grid.about-collage-grid--n4,
    .about-collage-grid.about-collage-grid--n5{
      grid-template-columns:1.3fr 1fr 1fr;
      gap:10px;
      aspect-ratio:3/3.6;
    }
  }
  /* Móviles muy estrechos (iPhone SE y similares): con el ancho de
     columna tan reducido, un mosaico de 3 columnas deja tiras demasiado
     finas. Se simplifica a una columna única con todas las fotos
     apiladas, que en tan poco ancho se lee mucho mejor que un mosaico. */
  @media (max-width:380px){
    .about-collage-grid.about-collage-grid--n4,
    .about-collage-grid.about-collage-grid--n5{
      display:flex;
      flex-direction:column;
      aspect-ratio:auto;
      gap:10px;
    }
    .about-collage-grid.about-collage-grid--n4 .about-collage-item,
    .about-collage-grid.about-collage-grid--n5 .about-collage-item{
      grid-area:auto !important;
      width:100%;
      aspect-ratio:4/3;
    }
  }

  .ba-card-frame{
    position:relative;
    width:100%;
    aspect-ratio:4/3;
    border-radius:22px;
    overflow:hidden;
    background:#111;
    border:1px solid rgba(255,255,255,0.16);
    box-shadow:
      0 18px 46px rgba(0,0,0,0.5),
      0 2px 10px rgba(0,0,0,0.35);
  }
  .ba-card-before,
  .ba-card-after{
    position:absolute;
    inset:0;
    width:100%;
    height:100%;
    /* FIX: antes era "cover", que recorta cada foto para rellenar el
       recuadro entero -y como "sin editar" y "editada" casi nunca tienen
       exactamente la misma proporción, cada una se recortaba/ampliaba una
       cantidad distinta para llenar el mismo hueco, así que una acababa
       viéndose más "de cerca" que la otra-. Con "contain" cada foto se ve
       entera, a su proporción real, sin que ninguna de las dos se amplíe
       más que la otra; si sobra hueco a los lados o arriba/abajo se rellena
       con el fondo oscuro de .ba-card-frame en vez de recortar la foto. */
    background-size:contain;
    background-repeat:no-repeat;
    background-position:center;
  }
  .ba-card-after-clip{
    position:absolute;
    inset:0;
    width:100%;
    height:100%;
    overflow:hidden;
  }
  .ba-card-handle{
    position:absolute;
    top:0;
    bottom:0;
    left:50%;
    width:2px;
    background:rgba(255,255,255,0.85);
    transform:translateX(-50%);
    display:flex;
    align-items:center;
    justify-content:center;
    /* La raya en sí ya no se puede arrastrar (ver más abajo, el arrastre
       vive solo en el círculo .ba-card-handle-grip): pointer-events:none
       aquí deja que los toques sobre la línea -fuera del círculo- pasen
       de largo hacia la foto de debajo, como si no estuviera. */
    pointer-events:none;
  }
  .ba-card-handle-grip{
    width:38px;
    height:38px;
    border-radius:50%;
    background:#fff;
    color:#111;
    display:flex;
    align-items:center;
    justify-content:center;
    box-shadow:0 4px 14px rgba(0,0,0,0.4);
    flex:none;
    /* Único punto de arrastre del comparador (ver crearComparador en
       before-after-card.js): solo aquí hace falta cursor de arrastre
       horizontal y bloquear el gesto táctil por defecto (si no,
       touch-action:auto haría que el propio navegador intentara
       interpretar el arrastre como scroll en vez de dejarlo al JS). */
    cursor:ew-resize;
    touch-action:none;
    pointer-events:auto;
  }
  .ba-card-label{
    position:absolute;
    top:14px;
    font-size:0.66rem;
    font-weight:600;
    letter-spacing:0.09em;
    text-transform:uppercase;
    color:#fff;
    background:rgba(10,10,10,0.55);
    backdrop-filter:blur(6px);
    -webkit-backdrop-filter:blur(6px);
    padding:6px 12px;
    border-radius:20px;
    pointer-events:none;
  }
  .ba-card-label--before{ left:14px; }
  .ba-card-label--after{ right:14px; }
  /* Mientras la nube todavía no ha respondido la primera vez (ver
     "cloudLoaded" en comparison-pairs.js), el recuadro no muestra ni una
     foto ni el degradado de "sin foto" -para no dar la sensación de que
     no se ha añadido nada-, sino este pulso neutro y sutil. */
  .ba-card-frame.is-loading{
    animation:baFrameLoading 1.4s ease-in-out infinite;
  }
  @keyframes baFrameLoading{
    0%, 100%{ background-color:#111; }
    50%{ background-color:#1c1c1c; }
  }
  @media (max-width:600px){
    .ba-card-frame{ aspect-ratio:3/4; border-radius:16px; }
    .ba-card-handle-grip{ width:32px; height:32px; }
  }

  /* ---------- Sección: Ajustes ---------- */
  /* Menú horizontal de pestañas: una por cada bloque de ajustes (Carrusel,
     los que se vayan añadiendo...). Solo se ve el contenido de la pestaña
     activa a la vez, en vez de todos los bloques apilados uno debajo de
     otro. Para añadir un ajuste nuevo: una pestaña más aquí + un panel más
     abajo con data-panel a juego. */
  .ajustes-tabs{
    display:flex;
    gap:4px;
    overflow-x:auto;
    -webkit-overflow-scrolling:touch;
    background:rgba(0,0,0,0.045);
    border:1px solid rgba(0,0,0,0.05);
    padding:5px;
    border-radius:14px;
    margin:24px 0 28px;
    scrollbar-width:none;
  }
  .ajustes-tabs::-webkit-scrollbar{ display:none; }
  .ajustes-tab{
    flex:0 0 auto;
    background:transparent;
    border:none;
    border-radius:10px;
    padding:9px 16px;
    margin-right:0;
    font-family:'Fraunces','Georgia',serif;
    font-weight:500;
    font-size:0.9rem;
    color:var(--dim);
    cursor:pointer;
    white-space:nowrap;
    transition:color .2s ease, background .2s ease, box-shadow .2s ease;
  }
  .ajustes-tab:hover{ color:var(--fg); }
  .ajustes-tab.active{
    color:var(--fg);
    font-style:normal;
    font-weight:600;
    background:#fff;
    box-shadow:0 2px 9px rgba(0,0,0,0.10);
  }
  .ajustes-panels{ position:relative; }
  .ajustes-panel{ display:none; }
  .ajustes-panel.active{
    display:block;
    animation:ajustesPanelIn .28s ease;
  }
  @keyframes ajustesPanelIn{
    from{ opacity:0; transform:translateY(4px); }
    to{ opacity:1; transform:translateY(0); }
  }

  .ajustes-section{
    border:1px solid rgba(0,0,0,0.07);
    border-radius:20px;
    padding:28px 26px;
    background:#fff;
    box-shadow:0 2px 14px rgba(0,0,0,0.045);
  }
  .ajustes-section-head{
    display:flex;
    align-items:flex-start;
    gap:14px;
    margin-bottom:20px;
  }
  .ajustes-section-icon{
    flex-shrink:0;
    width:40px;
    height:40px;
    border-radius:12px;
    background:linear-gradient(155deg, rgba(255,90,31,0.14), rgba(255,90,31,0.06));
    color:var(--accent);
    display:flex;
    align-items:center;
    justify-content:center;
    box-shadow:inset 0 0 0 1px rgba(255,90,31,0.1);
  }
  .ajustes-section-icon svg{ width:19px; height:19px; }
  .ajustes-section-title{
    font-family:'Fraunces','Georgia',serif;
    font-weight:600;
    font-size:1.08rem;
    color:var(--fg);
    margin:0 0 4px;
    display:flex;
    align-items:center;
    gap:10px;
    flex-wrap:wrap;
    line-height:1.3;
  }
  .ajustes-section-desc{
    font-size:0.88rem;
    line-height:1.55;
    color:var(--dim);
    margin:0;
  }
  .ajustes-section-footer{
    margin-top:6px;
  }
  .ajustes-note{
    background:rgba(0,0,0,0.022);
    border:1px solid rgba(0,0,0,0.06);
    border-left:3px solid var(--accent);
    border-radius:10px;
    padding:16px 18px;
    margin:0 0 24px;
  }
  .ajustes-note p{
    margin:0;
    font-size:0.9rem;
    line-height:1.6;
    color:var(--dim);
  }
  .ajustes-note p + p{ margin-top:8px; }

  /* Botón de subir: tarjeta con icono en círculo + etiqueta, en vez del
     antiguo recuadro punteado con solo texto en cursiva, que quedaba muy
     pobre. */
  .ajustes-uploader{
    border:1.5px dashed rgba(0,0,0,0.16);
    border-radius:16px;
    padding:30px 20px;
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:10px;
    text-align:center;
    cursor:pointer;
    background:rgba(0,0,0,0.012);
    transition:background .2s ease, border-color .2s ease, transform .18s ease;
  }
  .ajustes-uploader:hover{
    background:rgba(255,90,31,0.05);
    border-color:rgba(255,90,31,0.45);
    transform:translateY(-1px);
  }
  .ajustes-uploader:active{ transform:translateY(0); }
  .ajustes-uploader input[type="file"]{ display:none; }
  .ajustes-uploader-icon{
    width:46px;
    height:46px;
    border-radius:50%;
    background:rgba(255,90,31,0.11);
    color:var(--accent);
    display:flex;
    align-items:center;
    justify-content:center;
  }
  .ajustes-uploader-icon svg{ width:21px; height:21px; }
  .ajustes-uploader .ajustes-uploader-label{
    font-family:'Fraunces','Georgia',serif;
    font-weight:600;
    font-style:normal;
    color:var(--fg);
    font-size:0.98rem;
  }
  .ajustes-uploader .ajustes-uploader-sub{
    display:block;
    font-size:0.76rem;
    letter-spacing:0.05em;
    text-transform:uppercase;
    color:var(--dim);
  }

  .ajustes-grid{
    display:grid;
    grid-template-columns:repeat(auto-fill, minmax(130px, 1fr));
    gap:16px;
    margin-top:24px;
  }
  .ajustes-thumb{
    position:relative;
    aspect-ratio:1/1;
    border-radius:12px;
    overflow:hidden;
    background:#f0f0ee;
    box-shadow:0 3px 12px rgba(0,0,0,0.09);
    cursor:grab;
  }
  .ajustes-thumb.is-dragging{ cursor:grabbing; }
  .ajustes-thumb img{
    width:100%;
    height:100%;
    object-fit:cover;
    display:block;
    touch-action:none;
    -webkit-user-drag:none;
    -webkit-touch-callout:none;
    -webkit-tap-highlight-color:transparent;
    user-select:none;
  }
  /* Pista visual de que la foto se puede arrastrar para reencuadrarla:
     un icono de "mover" que solo aparece al pasar el ratón. En pantallas
     táctiles no hay hover real, así que se muestra un icono fijo y
     pequeño en la esquina en su lugar (ver .ajustes-thumb-hint). */
  @media (hover:hover) and (pointer:fine){
    .ajustes-thumb-move{
      position:absolute;
      inset:0;
      display:flex;
      align-items:center;
      justify-content:center;
      background:rgba(0,0,0,0);
      opacity:0;
      pointer-events:none;
      transition:opacity .2s ease, background .2s ease;
    }
    .ajustes-thumb:hover .ajustes-thumb-move{
      opacity:1;
      background:rgba(0,0,0,0.16);
    }
  }
  @media not all and (hover:hover) and (pointer:fine){
    .ajustes-thumb-move{ display:none; }
    .ajustes-thumb-hint{
      position:absolute;
      left:7px;
      bottom:7px;
      width:22px;
      height:22px;
      border-radius:50%;
      background:rgba(0,0,0,0.4);
      display:flex;
      align-items:center;
      justify-content:center;
      pointer-events:none;
    }
    .ajustes-thumb-hint svg{ width:12px; height:12px; color:#fff; }
  }
  @media (hover:hover) and (pointer:fine){
    .ajustes-thumb-hint{ display:none; }
  }
  .ajustes-thumb-move svg{
    width:20px;
    height:20px;
    color:#fff;
    filter:drop-shadow(0 1px 3px rgba(0,0,0,0.5));
  }
  .ajustes-thumb-wrap{
    display:flex;
    flex-direction:column;
    gap:7px;
  }
  .ajustes-thumb-name{
    width:100%;
    border:1px solid rgba(0,0,0,0.13);
    border-radius:9px;
    padding:7px 10px;
    font-family:'Fraunces','Georgia',serif;
    font-style:italic;
    font-size:0.8rem;
    color:var(--fg);
    background:#fff;
    transition:border-color .2s ease, box-shadow .2s ease;
  }
  .ajustes-thumb-name:focus{
    outline:none;
    border-color:rgba(255,90,31,0.5);
    box-shadow:0 0 0 3px rgba(255,90,31,0.1);
  }
  .ajustes-thumb-name::placeholder{ color:var(--dim); opacity:0.7; }
  .ajustes-thumb button{
    position:absolute;
    top:7px;
    right:7px;
    width:26px;
    height:26px;
    border-radius:50%;
    border:none;
    background:rgba(255,255,255,0.95);
    color:#222;
    box-shadow:0 2px 7px rgba(0,0,0,0.2);
    cursor:pointer;
    display:flex;
    align-items:center;
    justify-content:center;
    transition:background .18s ease, color .18s ease, transform .15s ease;
  }
  .ajustes-thumb button svg{ width:12px; height:12px; }
  .ajustes-thumb button:hover{ background:var(--accent); color:#fff; transform:scale(1.08); }
  .ajustes-empty{
    font-size:0.85rem;
    font-style:italic;
    color:var(--dim);
    margin-top:18px;
  }
  .ajustes-clear{
    display:inline-flex;
    align-items:center;
    gap:8px;
    margin-top:20px;
    background:#fff;
    border:1px solid rgba(0,0,0,0.14);
    border-radius:999px;
    padding:10px 20px;
    font-family:'Fraunces','Georgia',serif;
    font-weight:500;
    font-size:0.84rem;
    color:var(--dim);
    cursor:pointer;
    transition:border-color .2s ease, color .2s ease, box-shadow .2s ease;
  }
  .ajustes-clear-icon{ flex:none; width:15px; height:15px; }
  .ajustes-clear:hover{
    border-color:rgba(255,90,31,0.5);
    color:var(--accent);
    box-shadow:0 2px 10px rgba(255,90,31,0.12);
  }

  /* ---------- 1ª posición: título "Proyectos" + galería que se expande
     ----------
     Bloque fijo, superpuesto sobre la foto de entrada (la misma parada
     donde vivía #sceneHint, "desliza"), anclado ABAJO A LA IZQUIERDA
     (ni centrado ni con plaquita detrás: solo el texto, con su propia
     sombra para leerse bien encima de cualquier zona de la foto). Solo
     se muestra en stepIndex === 0: la clase "visible" la pone/quita
     scroll-engine.js con el mismo criterio que usa para #sceneHint
     (buscar "proyectosHeaderEl" en ese archivo). */
  .proyectos-header{
    position:fixed;
    bottom:6%;
    left:6%;
    z-index:20;
    width:min(88vw, 700px);
    display:flex;
    flex-direction:column;
    align-items:flex-start;
    opacity:0;
    /* Importante: SIN esto, aunque el bloque esté invisible (opacity:0)
       seguía "tapando" con su hueco fijo los toques de otras zonas de la
       página (p.ej. el tirador del comparador de "Mis ediciones"),
       porque un elemento con opacity:0 sigue recibiendo eventos si no
       se le quita pointer-events explícitamente. Solo su hijo
       #asExpandWrap (ver .as-expand más abajo, pointer-events:auto)
       vuelve a aceptar toques, para poder tocar las tarjetas. */
    pointer-events:none;
    /* SIN transition en opacity: el propio JS (updateProyectosFade, ver
       scroll-engine.js) ya recalcula y fija la opacidad en cada fotograma
       de la animación con su propia curva rápida; una transición CSS
       encima de eso perseguía un valor que cambia 60 veces por segundo y
       el resultado era un desaparecer visiblemente más lento de lo que
       marca esa curva. */
  }
  .proyectos-header.visible{ opacity:1; }
  /* Solo el texto, sin plaquita/recuadro detrás: cursiva serif (mismo
     estilo de familia que el resto de títulos de la web), blanco con
     una sombra suave para que se lea igual de bien encima de cualquier
     zona de la foto, clara u oscura. */
  .proyectos-title{
    margin:0 0 clamp(8px, 1.4vh, 14px);
    font-family:'Fraunces','Georgia',serif;
    font-weight:600;
    font-style:italic;
    font-size:clamp(1.15rem, 3.4vw, 1.6rem);
    letter-spacing:0.02em;
    color:#111111;
    text-shadow:0 1px 3px rgba(255,255,255,0.35), 0 2px 14px rgba(255,255,255,0.25);
    text-align:left;
  }
  @media (max-width:480px){
    .proyectos-header{ bottom:4%; left:5%; width:90vw; }
    .proyectos-title{ font-size:clamp(1rem, 4.6vw, 1.3rem); }
  }
  /* ---------- Escritorio / iPad horizontal (≥901px): TODO el bloque
     ("Fotos" + botones + "Proyectos" + galería) vive junto, en una sola
     columna bien estructurada, anclada justo a la derecha de la persona
     de la foto (que ocupa aprox. la mitad izquierda de la pantalla) sin
     taparla. Tamaño equilibrado: ni diminuto ni exagerado, pensado para
     verse bien tanto en un iPad en horizontal (~1024px) como en un
     monitor de escritorio grande. Usamos "left" (en vez de "right" o un
     truco de "transform" para centrar) porque así el punto de anclaje
     es siempre el mismo, fijo y predecible, sin arrastrar el problema
     de que un "transform" en este contenedor "atrape" a sus hijos con
     position:fixed -por eso "Fotos" (ver .as-cat-top) puede vivir
     dentro del flujo normal de este bloque sin más complicaciones-.
     Solo en pantallas grandes; en móvil se queda como estaba (pegado a
     la izquierda y abajo). ---------- */
  @media (min-width:901px){
    .proyectos-header{
      left:60%;
      right:auto;
      bottom:auto;
      top:14%;
      width:min(34vw, 620px);
    }
    .proyectos-title{ font-size:clamp(1.8rem, 2.6vw, 2.4rem); margin-top:clamp(24px, 4vh, 50px); }
    .as-expand{ width:min(34vw, 620px); }
    .as-expand-grid{ height:360px; }
  }
  /* .proyectos-body ya no necesita position propia en escritorio: al no
     haber ningún "transform" en #proyectosHeader, un fixed descendiente
     (como .as-cat-top) se sigue colocando con normalidad respecto a la
     ventana, así que aquí basta con dejar que fluya como una columna
     normal, justo debajo de "Fotos". */
  .proyectos-body{ display:contents; }
  @media (min-width:901px){
    .proyectos-body{ display:flex; flex-direction:column; align-items:flex-start; }
  }

  /* ---------- Título grande "Mi material / de trabajo" + frase (aparecen
     arriba del todo, en el último fotograma, justo antes de las cámaras)
     ----------
     Los dos viven dentro de .equipo-header, un único bloque fijo y
     centrado horizontalmente.

     El título se escribe en DOS líneas fijas ("Mi material" / "de
     trabajo", ver .equipo-title-line en el HTML) en vez de dejar que el
     texto se parta solo donde le toque: así se puede hacer mucho más
     grande sin miedo a que el salto de línea caiga en un sitio raro en
     algún ancho intermedio.

     Diseño en dos capas según el espacio disponible:
     - Por defecto (móvil/tablet, en columna): título arriba, frase debajo,
       todo centrado -flujo normal, con margin-top, así nunca se solapan
       aunque cambie la altura del título-.
     - Escritorio (≥901px, ver media query más abajo): .equipo-header pasa
       a ser una fila; el título queda a la izquierda y la frase a la
       derecha, más pequeña, alineada a la izquierda y con una línea vertical
       fina de separación, como una nota al margen -"bien chula"-, en vez de
       apilada debajo de un título ya de por sí enorme. */
  .equipo-header{
    position:fixed;
    top:11%;
    left:50%;
    transform:translateX(-50%);
    z-index:30;
    width:min(92vw, 760px);
    display:flex;
    flex-direction:column;
    align-items:center;
    pointer-events:none;
  }
  .equipo-title{
    position:relative;
    margin:0;
    max-width:100%;
    transform:translateY(-14px) scale(0.96);
    font-family:'Fraunces','Georgia',serif;
    font-weight:700;
    font-style:italic;
    /* Mucho más grande que antes (clamp anterior tope 4.6rem): al ir
       siempre en dos líneas fijas, puede crecer bastante más sin riesgo
       de desbordar ni de partirse en un sitio feo. */
    font-size:clamp(2.8rem, 9vw, 6.4rem);
    letter-spacing:0.01em;
    line-height:0.98;
    color:var(--fg);
    text-align:center;
    opacity:0;
    /* Antes .7s: el contenido tardaba en aparecer aunque el salto ya
       hubiera terminado. Se acorta para que se note casi al instante en
       cuanto se llega a la última parada. */
    transition:opacity .3s ease, transform .3s ease;
  }
  /* Cada línea es su propio bloque, así el salto entre "Mi material" y
     "de trabajo" es siempre exactamente ahí, en cualquier ancho de
     pantalla, nunca depende de si el texto "cabe" o no. */
  .equipo-title-line{ display:block; }
  .equipo-title::after{
    content:'';
    display:block;
    width:72px;
    height:5px;
    margin:18px auto 0;
    border-radius:2px;
    background:var(--accent);
  }
  .equipo-title.visible{
    opacity:1;
    /* --pe (post-end): mismo desplazamiento fluido que usa .camera-carousel,
       para que el título suba junto con el carrusel (todo el contenido del
       último bloque se mueve como una sola pieza al seguir el gesto más
       allá de la última parada). Ya no lleva translateX (el centrado
       horizontal ahora lo hace .equipo-header una sola vez para todo el
       bloque). */
    transform:translateY(calc(0px - var(--pe, 0px))) scale(1);
  }

  /* Frase corta: en móvil/tablet va debajo del título (flujo normal, con
     margin-top -así siempre queda igual de despegada del título, se
     parta este donde se parta-); en escritorio (ver media query) pasa a
     ir al lado derecho. Mismo bloque flexible y mismo movimiento (--pe)
     que el título y el carrusel, para que suban juntos como una sola
     pieza. Tipografía más discreta (sans, no Fraunces) para no competir
     con el título en cursiva. */
  .equipo-subtitle{
    position:relative;
    margin:0;
    margin-top:clamp(18px, 3.6vw, 32px);
    max-width:min(90vw, 480px);
    transform:translateY(-14px);
    font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
    font-size:clamp(0.85rem, 2.1vw, 1rem);
    line-height:1.5;
    letter-spacing:0.01em;
    color:var(--dim);
    text-align:center;
    opacity:0;
    transition:opacity .3s ease, transform .3s ease;
  }
  .equipo-subtitle.visible{
    opacity:1;
    transform:translateY(calc(0px - var(--pe, 0px)));
  }

  /* Móviles muy estrechos (320-480px): el título sube un poco en la
     pantalla para dejar más aire por debajo antes del carrusel. */
  @media (max-width:480px){
    .equipo-header{ top:7.5%; width:94vw; }
    .equipo-title{ font-size:clamp(2.1rem, 13.5vw, 3.1rem); }
    .equipo-title::after{ width:50px; height:4px; margin-top:12px; }
    .equipo-subtitle{ font-size:0.83rem; margin-top:16px; }
  }
  /* Foldables / móviles muy angostos (≤340px): un pelín más compacto
     todavía, para que el título quepa sin apretarse. */
  @media (max-width:340px){
    .equipo-title{ font-size:clamp(1.8rem, 14vw, 2.6rem); }
    .equipo-subtitle{ font-size:0.78rem; }
  }
  /* Tablets/portátiles medianos (siguen en columna, como el móvil, pero
     con paso intermedio de tamaño para que el título no salte de golpe
     al de escritorio). */
  @media (min-width:481px) and (max-width:900px){
    .equipo-header{ top:9%; }
    .equipo-title{ font-size:clamp(3rem, 10vw, 4.4rem); }
  }
  /* ---------- Escritorio (≥901px): la frase se coloca "bien chula" al
     lateral derecho del título en vez de apilada debajo -de lo contrario,
     con el título ya tan grande, quedaría todo demasiado alto y separado
     de las cámaras-. .equipo-header pasa de columna a fila; el título
     queda alineado a la izquierda y la frase, más pequeña y también
     alineada a la izquierda, se separa con una fina línea vertical a
     modo de nota al margen. */
  @media (min-width:901px){
    .equipo-header{
      flex-direction:row;
      align-items:center;
      justify-content:center;
      width:min(90vw, 1040px);
      gap:clamp(32px, 4.5vw, 64px);
    }
    .equipo-title{ text-align:left; flex:0 0 auto; }
    .equipo-title::after{ margin:18px 0 0; }
    .equipo-subtitle{
      margin-top:0;
      padding-left:clamp(24px, 2.6vw, 36px);
      border-left:1px solid rgba(17,17,17,0.16);
      text-align:left;
      max-width:240px;
      flex:0 0 auto;
    }
  }
  /* Monitores muy anchos: el título ya no sigue creciendo con el ancho
     (se queda estable en su tope) y el bloque entero se posiciona un
     poco más arriba para dejar más recorrido de aire hasta las cámaras. */
  @media (min-width:1600px){
    .equipo-header{ top:12%; width:min(66vw, 1120px); }
    .equipo-subtitle{ max-width:280px; font-size:1.05rem; }
  }

  /* ---------- Variante "a la derecha" ----------
     En escritorio la foto queda pegada a la izquierda (drawCover, dx:0),
     así que a la derecha suele quedar una franja libre. Antes el bloque
     título+frase se centraba siempre respecto a TODA la pantalla, así que
     en pantallas anchas terminaba invadiendo la propia foto/cámaras (ver
     captura reportada). Esta clase la añade el JS (positionEquipoHeader)
     solo cuando de verdad hay hueco de sobra a la derecha de la foto: en
     vez de centrado, el bloque se coloca DENTRO de esa franja libre, en
     columna y alineado a la izquierda (como una nota editorial junto a la
     imagen), y el JS fija su "left"/"width"/"top" en px según el hueco
     real disponible. Con !important porque hay que ganarle a las reglas
     de fila/centrado de los media queries de arriba, pensadas para el
     caso en que el título va centrado sobre la foto. */
  .equipo-header.equipo-header--side{
    flex-direction:column !important;
    align-items:flex-start !important;
    justify-content:center !important;
    transform:translateY(-50%) !important;
    text-align:left;
  }
  .equipo-header--side .equipo-title{
    text-align:left !important;
    flex:0 0 auto;
    /* Columna más estrecha que toda la pantalla: el título no necesita
       crecer tanto como cuando iba centrado sobre la foto entera. */
    font-size:clamp(1.7rem, 2.6vw, 2.9rem);
  }
  .equipo-header--side .equipo-title::after{ margin:16px 0 0; }
  .equipo-header--side .equipo-subtitle{
    margin-top:clamp(16px, 1.8vw, 26px) !important;
    padding-left:0 !important;
    border-left:none !important;
    text-align:left !important;
    max-width:100% !important;
    flex:0 0 auto;
  }
  /* Pantallas bajas (móvil en horizontal, ventanas de escritorio poco
     altas): con poca altura disponible, se sube y encoge todo el bloque
     para que nunca choque con el carrusel de cámaras de más abajo. En
     horizontal, aunque el ancho ya sea "de escritorio", conviene volver
     a la fila si hay ancho de sobra (ver la combinación con el media de
     ≥901px, que sigue aplicando) pero mucho más compacta. */
  @media (max-height:520px){
    .equipo-header{ top:5%; }
    .equipo-title{ font-size:clamp(1.6rem, 6.5vw, 2.6rem); }
    .equipo-subtitle{ margin-top:10px; font-size:0.78rem; }
  }

  /* ---------- Título de la 4ª posición ("Mis ediciones") ----------
     La franja negra que se descubre al seguir deslizando más allá de la
     3ª posición (después del carrusel de cámaras) pasa a tratarse como
     una parada más -su propia "página"-, con un título grande, mismo
     estilo tipográfico que .equipo-title (Fraunces, cursiva, negrita,
     con el mismo subrayado en --accent), pero en un tono claro para
     leerse sobre el fondo oscuro de .after-story-bg.

     No usa opacity para aparecer: arranca por debajo de toda la pantalla
     y, exactamente igual que el carrusel/título de la 3ª posición, se
     desplaza hacia arriba lo mismo que el dedo/rueda a través de la MISMA
     variable --pe (ver applyPostEndOffset en el JS). top:114vh hace que
     llegue a su posición final ya asentada arriba de la pantalla (algo
     más arriba que la altura donde antes quedaba el carrusel de cámaras)
     justo cuando el panel negro se descubre del todo (pe == 100vh) -así
     nunca se solapa con la foto/escena de delante, sin necesidad de
     calcular ninguna transición de opacidad aparte. */
  .after-story-header{
    position:fixed;
    top:105vh;
    left:50%;
    /* Más ancho que antes (760px) para dar sitio real al comparador
       agrandado (.as-ba-gallery, ver más abajo) y que quede bien
       centrado en vez de recortado por este contenedor. */
    width:min(94vw, 960px);
    transform:translate(-50%, calc(-1 * var(--pe, 0px)));
    z-index:5;
    text-align:center;
    pointer-events:none;
    /* Ahora aloja TODO el contenido de la 4ª posición (título, frase,
       comparador, categorías y CTA), no solo el título: pasa a ser una
       columna flexible con separación uniforme entre bloques, todo
       centrado y ajustándose solo según el hueco disponible (clamp con
       vh, ver cada bloque más abajo). */
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:clamp(14px, 2.6vh, 26px);
    /* Más aire debajo del último bloque (la galería que se expande) antes
       de que empiece lo que venga después (carrusel de cámaras): sin este
       hueco todo quedaba demasiado pegado al borde inferior. */
    padding-bottom:clamp(28px, 6vh, 70px);
  }
  .after-story-title{
    margin:0;
    font-family:'Fraunces','Georgia',serif;
    font-weight:700;
    font-style:italic;
    /* Se reduce el tope respecto al original (antes hasta 6.2rem): con
       todo el bloque nuevo debajo (comparador, categorías, CTA) hace
       falta dejar hueco, así que el título pasa a ser algo más discreto,
       en línea con el resto de proporciones del diseño de referencia. */
    font-size:clamp(2.3rem, 7.5vw, 4.2rem);
    letter-spacing:0.01em;
    line-height:1;
    color:#111111;
  }
  .after-story-title .as-word-light{ color:#111111; }
  .after-story-title .as-word-accent{ color:var(--accent); }
  .after-story-title::after{
    content:'';
    display:block;
    width:80px;
    height:5px;
    margin:16px auto 0;
    border-radius:2px;
    background:var(--accent);
  }
  @media (max-width:480px){
    .after-story-title{ font-size:clamp(2rem, 11vw, 2.8rem); }
    .after-story-title::after{ width:56px; height:4px; margin-top:12px; }
  }

  /* ---------- Frase corta bajo el título ("Transformo ideas en
     imágenes") ---------- */
  .after-story-sub{
    margin:0;
    font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
    font-size:clamp(0.66rem, 1.7vw, 0.8rem);
    font-weight:600;
    letter-spacing:0.16em;
    text-transform:uppercase;
    line-height:1.6;
    color:rgba(17,17,17,0.62);
  }
  .after-story-sub .as-accent-word{ color:var(--accent); }

  /* ---------- Comparador antes/después ----------
     Las flechas (as-ba-nav) viven fuera del recuadro, en la fila de pie
     de foto (.as-ba-footer, junto a las etiquetas), así que este
     contenedor no necesita reservarles ningún margen lateral: el
     recuadro puede aprovechar todo el ancho disponible. */
  .as-ba-gallery{
    position:relative;
    width:100%;
    /* Recuadro mucho más grande y más centrado: antes 720px de ancho
       tope; el bloque padre (.after-story-header) también se ensancha
       (ver más abajo) para que este margen extra tenga sitio real. */
    max-width:920px;
    margin:0 auto;
    pointer-events:auto;
    /* Un único número para el alto máximo de la foto, compartido por el
       recuadro (.ba-card-frame) y por el escenario que lo envuelve
       (.as-ba-stage, más abajo): así los dos se mueven siempre a la vez
       si algún día cambia este valor, en vez de tener que acordarse de
       actualizarlo en dos sitios. */
    --as-ba-frame-max-h: clamp(260px, 66vh, 680px);
  }
  .as-ba-gallery .ba-card-frame{
    /* Proporción de repuesto: se usa solo mientras no hay ninguna foto
       real todavía (el recuadro muestra los degradados de "sin foto") o
       si por lo que sea la foto no llega a cargar. En cuanto hay una foto
       real, la clase .has-photo (más abajo) toma el control del tamaño. */
    aspect-ratio:4/3;
    /* Tope de alto más generoso: cuanto más alto se le deja crecer, más
       ancho puede llegar a ocupar una foto vertical antes de tocar ese
       tope (aspect-ratio auto, ver .has-photo más abajo) -así aprovecha
       el margen lateral que le sobraba en vez de quedarse pequeña. Una
       foto horizontal nunca se sale de la pantalla pase lo que pase,
       porque sigue topando primero con max-width:100% (ver .has-photo). */
    max-height:var(--as-ba-frame-max-h);
    border-radius:18px;
  }
  /* ---------- Escenario de altura fija ----------
     Hueco con SIEMPRE la misma altura (el mismo número que el tope de
     arriba), donde se centra el recuadro -sea cual sea la forma real de
     la foto de dentro-. Antes la fila de botones de abajo (.as-ba-footer)
     iba justo después del recuadro en el flujo normal, así que su
     posición vertical dependía de lo alta que fuera cada foto y "saltaba"
     al cambiar de pareja; al vivir siempre justo después de este hueco de
     altura constante, se queda fija en el mismo sitio pase lo que pase. */
  .as-ba-stage{
    /* position:relative: ancla real de las flechas (as-ba-nav, ver más
       abajo) -su altura NUNCA cambia (height:var(--as-ba-frame-max-h)),
       así que un top:50% aquí es siempre la misma "media altura", pase lo
       que pase con el tamaño de la foto de cada pareja. */
    position:relative;
    width:100%;
    display:flex;
    /* Foto centrada verticalmente dentro del hueco fijo: antes iba anclada
       abajo (align-items:flex-end) para que el margen en blanco -si la
       foto era más baja que el máximo, una panorámica por ejemplo- no
       cayera pegado a la fila de botones de abajo (entonces las flechas
       vivían en el pie de foto, debajo del recuadro). Ahora que las
       flechas ya NO están debajo sino ancladas a los LATERALES de este
       mismo escenario (as-ba-nav, top:50% de aquí abajo) ese problema ya
       no aplica -y además centrar la foto es lo que hace que su mitad
       real coincida con el top:50% de las flechas, en vez de con la
       mitad de un hueco vacío-. */
    align-items:center;
    justify-content:center;
    height:var(--as-ba-frame-max-h);
  }
  /* ---------- Envoltorio del recuadro: efecto "hay más fotos detrás"
     ----------
     Se amolda exactamente al tamaño real de .ba-card-frame (mismo truco
     que ya usa .has-photo: ver más abajo) y, como NO recorta su
     contenido, dos fotos "fantasma" del mismo tamaño pueden asomar por
     los bordes, giradas ligeramente a cada lado -como una pila de fotos
     físicas apoyadas una sobre otra-. Van DETRÁS (z-index negativo) y no
     reaccionan al ratón/toque. */
  .as-ba-photo-wrap{
    position:relative;
    isolation:isolate;
    width:100%;
    /* Siempre centrado horizontalmente dentro de .as-ba-card, tenga la
       foto la forma que tenga: antes se centraba solo mientras el ancho
       era 100% (sin foto) -en cuanto sizeFrameToPhoto (JS) le pone un
       ancho exacto en píxeles, sin este margin:auto se quedaba pegado al
       borde izquierdo en vez de centrado-. */
    margin:0 auto;
  }
  /* El ancho exacto de este envoltorio (para que las "fotos fantasma" de
     detrás coincidan con el tamaño real de la foto, y no con el ancho
     completo del contenedor) lo pone directamente en JS sizeFrameToPhoto
     (ver comparison-pairs.js), con el mismo número de píxeles que el
     recuadro. Antes se intentaba con un selector CSS :has(), que no
     todos los móviles soportaban igual de bien -de ahí que a veces el
     recuadro se viera con el tamaño equivocado-; calcularlo a mano en JS
     funciona igual en cualquier dispositivo. */
  .as-ba-photo-wrap::before,
  .as-ba-photo-wrap::after{
    content:'';
    position:absolute;
    inset:0;
    border-radius:18px;
    background:linear-gradient(155deg, #2a2a2a, #131313 70%);
    border:1px solid rgba(255,255,255,0.1);
    box-shadow:0 14px 34px rgba(0,0,0,0.4);
    z-index:-1;
    pointer-events:none;
  }
  .as-ba-photo-wrap::before{
    transform:rotate(-3.5deg) translate(-10px, 10px);
    opacity:0.85;
  }
  .as-ba-photo-wrap::after{
    transform:rotate(3.5deg) translate(10px, 12px);
    opacity:0.65;
    z-index:-2;
  }
  @media (max-width:480px){
    /* Ahora que las flechas ya no viven en el borde (se movieron al
       centro, junto a las etiquetas), el contenedor puede usar casi todo
       el ancho de la pantalla sin ningún riesgo de gesto de borde -antes
       94vw era además la restricción real que limitaba el tamaño de la
       foto (por delante incluso del alto máximo), así que ensancharlo es
       lo que de verdad la hace más grande en móvil-.
       Se sube también el ancho de todo el bloque (título, foto, etc.). */
    .after-story-header{ width:98vw; }
    /* Todo lo grande que permita el dispositivo: se apoya en dvh (alto
       real visible, sin que la barra de Safari lo falsee) en vez de vh a
       secas, y con un tope bastante generoso -aquí "grande" gana sobre
       "conservador"-. aspect-ratio 5/4 es solo el valor de repuesto sin
       foto real; en cuanto hay foto, sizeFrameToPhoto (JS) fija el ancho
       y el alto exactos como estilo propio del recuadro, así que manda
       por encima de este aspect-ratio -pero sigue leyendo este mismo
       max-height como su tope real, así que el alto máximo alcanzable en
       móvil se define aquí igual que antes-. */
    .as-ba-gallery{ --as-ba-frame-max-h:64vh; --as-ba-frame-max-h:64dvh; }
    .as-ba-gallery .ba-card-frame{ aspect-ratio:5/4; }
    .as-ba-photo-wrap::before,
    .as-ba-photo-wrap::after{ border-radius:14px; }
    .as-ba-photo-wrap::before{ transform:rotate(-3deg) translate(-7px, 7px); }
    .as-ba-photo-wrap::after{ transform:rotate(3deg) translate(7px, 9px); }
  }
  /* Tirador más disimulado en el comparador de "Mis ediciones": círculo
     más pequeño, más transparente y con menos sombra que el estándar
     (.ba-card-handle-grip, compartido con la galería de "Ediciones" del
     menú, que se deja tal cual). */
  .as-ba-gallery .ba-card-handle-grip{
    width:26px;
    height:26px;
    background:rgba(255,255,255,0.55);
    box-shadow:0 2px 8px rgba(0,0,0,0.18);
  }
  .as-ba-gallery .ba-card-handle{
    background:rgba(255,255,255,0.55);
  }
  /* ---------- Pie de foto: solo etiquetas ----------
     Las flechas ya no van aquí -se movieron a los laterales del recuadro,
     ver ".as-ba-stage .as-ba-nav" más abajo-; esta fila ahora solo
     centra SIN EDITAR/EDITADA debajo de la foto. */
  .as-ba-footer{
    display:flex;
    align-items:center;
    justify-content:center;
    width:fit-content;
    margin:12px auto 0;
  }
  .as-ba-labels{
    display:flex;
    justify-content:center;
    align-items:center;
    /* Antes con gap:10px; ahora un pelín más, ya que las flechas se han
       acercado mucho (as-ba-footer) y conviene que "SIN EDITAR" y
       "EDITADA" sigan distinguiéndose bien entre sí como grupo aparte. */
    gap:14px;
    /* flex:1 ya no hace falta: antes empujaba las flechas hasta los
       extremos de un footer a ancho completo (100%); ahora el footer es
       "de su contenido" (ver as-ba-footer) así que este hueco solo debe
       medir lo que ocupa el propio texto. */
  }
  /* Ahora sin recuadro/pastilla: solo texto, con una micro-marca de color a
     la izquierda (un guioncito) que diferencia "sin editar" (gris neutro)
     de "editada" (color de acento) de un vistazo, sin necesitar ningún
     fondo -más limpio y ligero que la pastilla rellena anterior-. */
  .as-ba-labels .ba-card-label{
    position:static;
    background:none;
    backdrop-filter:none;
    -webkit-backdrop-filter:none;
    padding:0;
    border-radius:0;
    box-shadow:none;
    display:inline-flex;
    align-items:center;
    gap:7px;
    font-size:0.68rem;
    font-weight:600;
    letter-spacing:0.12em;
  }
  .as-ba-labels .ba-card-label::before{
    content:'';
    width:11px;
    height:1.5px;
    flex:none;
  }
  .as-ba-labels .ba-card-label--before{
    color:var(--dim);
  }
  .as-ba-labels .ba-card-label--before::before{
    background:var(--dim);
  }
  .as-ba-labels .ba-card-label--after{
    color:var(--accent);
  }
  .as-ba-labels .ba-card-label--after::before{
    background:var(--accent);
  }
  /* FIX 2: en cuanto hay foto real, sizeFrameToPhoto (comparison-pairs.js)
     calcula su ancho y su alto exactos en píxeles -respetando siempre el
     tope de alto de abajo y el ancho disponible, sin recortar nunca- y
     los escribe directamente como estilo del propio recuadro; por eso
     aquí ya no hace falta ni "aspect-ratio:auto" ni el truco de
     "width:max-content" (dependía de que el navegador resolviera un
     cálculo de tamaño encadenado con una imagen oculta, y en algunos
     móviles no terminaba de encajar: a veces la foto no llegaba a verse,
     otras veces se recortaba). max-width:100% se deja solo como red de
     seguridad por si el cálculo en JS aún no ha llegado. */
  .as-ba-gallery .ba-card-frame.has-photo{
    max-width:100%;
    margin:0 auto;
  }
  /* ---------- Flechas de navegación (siguiente/anterior pareja) ----------
     FUERA de la imagen y siempre a la misma altura: ancladas con
     position:absolute a .as-ba-stage (no a .as-ba-photo-wrap, que se
     amolda a cada foto y por tanto cambiaría de tamaño/posición de una
     pareja a otra -ver comentario en .as-ba-stage-). Como .as-ba-stage
     tiene una altura fija (el "hueco" que ya existía para que la fila de
     abajo no saltase), top:50% aquí equivale a "la mitad del recuadro",
     siempre en el mismo sitio, sea la foto vertical, horizontal o
     cuadrada. En horizontal se anclan al borde del ESCENARIO completo (que
     ocupa todo el ancho del comparador), no al de la foto -que puede ser
     más estrecha si es vertical-, así quedan SIEMPRE fuera de la imagen,
     nunca encima ni tapándola. */
  .as-ba-stage .as-ba-nav{
    position:absolute;
    top:50%;
    transform:translateY(-50%);
    z-index:5;
    left:0;
  }
  .as-ba-stage .as-ba-nav--prev{ left:0; }
  .as-ba-stage .as-ba-nav--next{ right:0; left:auto; }
  .as-ba-nav{
    flex:none;
    /* Mismo espacio de toque que antes (42px), pero ya SIN el círculo de
       fondo: ahora el botón es transparente del todo y lo único visible
       es la propia flecha (el SVG, ver más abajo) centrada dentro de ese
       mismo hueco -por eso el tamaño total no cambia, solo se le quita el
       "envoltorio" visual-. */
    width:42px;
    height:42px;
    border:none;
    background:none;
    display:flex;
    align-items:center;
    justify-content:center;
    cursor:pointer;
    padding:0;
    color:#000;
    transition:opacity .2s ease, transform .2s ease;
    /* Refuerzo para móvil: no fiarse de que pointer-events:auto llegue
       heredado desde el ancestro (.as-ba-gallery) -aquí se fija de forma
       directa e inequívoca sobre el propio botón-. touch-action:manipulation
       evita que Safari/iOS espere a descartar un doble-toque de zoom antes
       de disparar el click (ese retraso, sumado a cualquier otro gesto de
       la página, es una causa típica de "botón que no responde solo en
       móvil"), y quita el resaltado gris que Safari pinta por defecto al
       tocar. */
    pointer-events:auto;
    touch-action:manipulation;
    -webkit-tap-highlight-color:transparent;
  }
  /* FIX: antes había una zona de toque invisible más grande que el
     círculo (::after con inset:-6px) para facilitar el acierto en móvil.
     Esa zona invisible se salía del propio círculo -y a veces del hueco
     entre botón y etiqueta- así que se sentía como un "botón fantasma":
     a veces tocar justo al lado del círculo (fuera de lo que se ve) SÍ
     lo activaba, y otras veces (fuera de ese margen) no pasaba nada o
     disparaba el gesto de deslizar la página. Ahora no hay ninguna zona
     extra: el hueco de 42px (invisible, sin círculo) es exactamente lo
     único que responde al toque, sin sorpresas. */
  .as-ba-nav svg{
    width:20px;
    height:20px;
    /* Sombra propia en vez de fondo: sin el círculo blanco de detrás, la
       flecha necesita algo que la haga legible sobre CUALQUIER foto (una
       foto clara la dejaría casi invisible si fuera solo blanco liso).
       drop-shadow dibuja un halo oscuro sutil alrededor del propio trazo
       -sigue exactamente la forma de la flecha, a diferencia de
       text-shadow/box-shadow, que dibujarían un cuadrado-. */
    filter:drop-shadow(0 1px 3px rgba(0,0,0,0.85)) drop-shadow(0 0 6px rgba(0,0,0,0.35));
  }
  .as-ba-nav:hover{ opacity:0.75; }
  .as-ba-nav:active{ opacity:0.55; transform:scale(0.9); }

  /* ---------- Botón de pantalla completa ----------
     Círculo pequeño en la esquina del recuadro (con su propio fondo
     semitransparente, a diferencia de as-ba-nav: aquí SÍ hace falta
     porque vive encima de la foto, no fuera de ella, y necesita
     distinguirse en cualquier imagen). Al pulsarlo, JS mueve el propio
     .as-ba-gallery (comparador completo, con barrita y flechas
     incluidas) a un overlay a pantalla completa -ver
     setAsBaFullscreen en comparison-pairs.js-. */
  .as-ba-expand{
    position:absolute;
    top:10px;
    right:10px;
    z-index:6;
    width:34px;
    height:34px;
    border:none;
    border-radius:50%;
    background:rgba(0,0,0,0.45);
    color:#fff;
    display:flex;
    align-items:center;
    justify-content:center;
    cursor:pointer;
    padding:0;
    pointer-events:auto;
    touch-action:manipulation;
    -webkit-tap-highlight-color:transparent;
    transition:opacity .2s ease, transform .2s ease, background .2s ease;
  }
  .as-ba-expand svg{ display:block; }
  .as-ba-expand:hover{ background:rgba(0,0,0,0.65); }
  .as-ba-expand:active{ transform:scale(0.9); }

  /* ---------- Overlay a pantalla completa ----------
     Mismo comparador, sacado del flujo normal (JS lo reparenta a
     <body>: .after-story-header, su contenedor de siempre, tiene un
     transform propio -ver .after-story-header más arriba- y un
     ancestro con transform rompe position:fixed para sus hijos, así
     que fixed aquí dentro no llegaría a cubrir toda la pantalla; por
     eso el JS lo mueve de sitio en vez de fijarlo tal cual). El resto
     -barrita de arrastre, flechas siguiente/anterior, etiquetas- es el
     mismo HTML/JS de siempre, solo que a un tamaño mucho mayor gracias
     a --as-ba-frame-max-h, que aquí se sobrescribe. */
  .as-ba-gallery.is-fullscreen{
    position:fixed;
    inset:0;
    z-index:3000;
    max-width:none;
    width:100vw;
    height:100vh;
    height:100dvh;
    margin:0;
    background:#000;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    padding:0;
    /* Sin pie de foto que reservar (las etiquetas se ocultan aquí
       debajo) y sin ningún margen: la foto ocupa el alto completo de
       la pantalla, al máximo. */
    --as-ba-frame-max-h: 100vh;
    --as-ba-frame-max-h: 100dvh;
  }
  .as-ba-gallery.is-fullscreen .as-ba-card{
    width:100%;
    height:100%;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
  }
  .as-ba-gallery.is-fullscreen .as-ba-stage{
    height:100%;
  }
  /* Sin margen ni esquinas redondeadas: aquí ya no es una "tarjeta"
     flotando sobre la página, es la foto a pantalla completa. */
  .as-ba-gallery.is-fullscreen .ba-card-frame{
    border-radius:0;
  }
  /* Sin flechas en pantalla completa: el usuario las pidió fuera para
     aprovechar el máximo tamaño posible (ya no hay que reservarles hueco
     a los lados, ver NAV_CLEARANCE en comparison-pairs.js). Para cambiar
     de pareja hay que salir y volver a entrar -es la foto sola, al
     máximo, lo que se pidió-. */
  .as-ba-gallery.is-fullscreen .as-ba-nav{
    display:none;
  }
  /* Efecto "hay más fotos detrás" (las dos copias giradas que asoman
     por los bordes, ver .as-ba-photo-wrap::before/::after más abajo):
     tiene sentido como adorno cuando el recuadro es una tarjeta más
     dentro de la página, pero aquí -foto sola, a pantalla completa, sin
     margen- ese "detrás" ya no cabe (se saldría de la pantalla) y
     además el usuario pidió expresamente que no aparente que hay más
     fotos. Se apaga solo en este modo. */
  .as-ba-gallery.is-fullscreen .as-ba-photo-wrap::before,
  .as-ba-gallery.is-fullscreen .as-ba-photo-wrap::after{
    display:none;
  }
  /* Pantalla completa = solo la foto y la barrita de arrastre, sin
     flechas (ver .as-ba-gallery.is-fullscreen .as-ba-nav más arriba) ni
     pie de foto. El pie con las etiquetas SIN EDITAR/EDITADA es aquí
     ruido de más, así que se oculta del todo. */
  .as-ba-gallery.is-fullscreen .as-ba-footer{
    display:none;
  }
  /* El propio botón se reposiciona a la esquina del VIEWPORT (ya no de
     la foto, que en pantalla completa puede ser más pequeña que el
     hueco disponible) y pasa a servir también de botón de cierre -el
     icono y el aria-label los cambia setAsBaFullscreen al abrir/cerrar-. */
  .as-ba-gallery.is-fullscreen .as-ba-expand{
    position:fixed;
    top:max(14px, env(safe-area-inset-top));
    right:max(14px, env(safe-area-inset-right));
    z-index:3002;
    width:40px;
    height:40px;
    background:rgba(255,255,255,0.12);
  }
  .as-ba-gallery.is-fullscreen .as-ba-expand:hover{ background:rgba(255,255,255,0.22); }
  /* Bloquea el scroll de la página de fondo mientras el overlay está
     abierto (lo pone/quita el mismo JS que reparenta el comparador). */
  body.as-ba-fullscreen-open{ overflow:hidden; }

  /* ---------- Tarjetas de "Proyectos en YouTube" ----------
     Se usan solo dentro de #view-mis-videos (ver .youtube-grid--full más
     abajo): cada tarjeta es un <a> que abre el vídeo real en YouTube en
     una pestaña nueva -no hay reproductor embebido aquí, solo la
     miniatura (ver getYoutubeThumb en youtube-videos.js)-. */
  .youtube-grid{
    display:flex;
    flex-wrap:wrap;
    justify-content:center;
    gap:clamp(16px, 2.6vw, 28px);
  }
  .youtube-card{
    display:block;
    width:clamp(260px, 38vw, 420px);
    text-decoration:none;
    color:inherit;
    /* Ligero levantamiento al pasar el ratón/tocar, igual que el resto de
       tarjetas interactivas de la web (ver .social-icon, .as-cat-btn). */
    transition:transform .2s ease;
  }
  .youtube-card:hover{ transform:translateY(-2px); }
  .youtube-card:active{ transform:translateY(0) scale(0.98); }
  .youtube-card-thumb{
    position:relative;
    width:100%;
    aspect-ratio:16/9;
    border-radius:12px;
    overflow:hidden;
    background-color:#e8e6e1; /* mismo gris neutro que el resto de placeholders "sin foto" de la web */
    background-size:cover;
    background-position:center;
    box-shadow:0 6px 18px rgba(0,0,0,0.16);
  }
  .youtube-card-title{
    margin:10px 0 0;
    font-family:'Fraunces','Georgia',serif;
    font-weight:500;
    font-size:clamp(0.82rem, 1.9vw, 0.92rem);
    line-height:1.35;
    color:#111111;
    text-align:center;
    /* Como máximo 2 líneas; si el título es más largo, se recorta con
       puntos suspensivos en vez de descuadrar la altura de la fila de
       tarjetas. */
    display:-webkit-box;
    -webkit-line-clamp:2;
    -webkit-box-orient:vertical;
    overflow:hidden;
  }
  /* Aviso de "toca para ver": fuera de la miniatura (que así se ve
     limpia y entera), como una línea pequeña y discreta debajo del
     título -mismo icono de play que antes, pero ahora en miniatura y
     acompañado de texto, sin cubrir ni un píxel del fotograma-. */
  .youtube-card-hint{
    display:flex;
    align-items:center;
    justify-content:center;
    gap:6px;
    margin-top:6px;
    color:var(--dim);
    opacity:0.85;
    transition:opacity .2s ease, color .2s ease;
  }
  .youtube-card:hover .youtube-card-hint{ opacity:1; color:var(--accent); }
  .youtube-card-hint svg{ width:14px; height:14px; flex:none; }
  .youtube-card-hint-text{
    font-family:inherit;
    font-size:0.72rem;
    font-weight:600;
    letter-spacing:0.02em;
    text-transform:uppercase;
  }
  @media (max-width:480px){
    /* En móvil la miniatura ocupa todo el ancho disponible (una sola
       columna), en vez de intentar encajar varias tarjetas pequeñas. */
    .youtube-card{ width:100%; max-width:480px; }
  }

  /* ---------- Cuadrícula completa dentro de #view-mis-videos ----------
     Mismas tarjetas (.youtube-card) que antes vivían sobre la escena;
     centradas en la página (a diferencia del resto de listados de esta
     subvista, aquí interesa que las tarjetas -más grandes que antes-
     queden centradas en vez de pegadas al margen izquierdo). */
  .youtube-grid--full{
    justify-content:center;
    margin-top:8px;
  }
  #view-mis-videos h1 .as-word-accent{ color:var(--accent); }

  /* ---------- Galería a pantalla completa de una categoría de "Proyectos"
     ----------
     El elemento ya vive de fábrica fuera de cualquier ancestro con
     transform (ver el comentario en index.html, junto a #asCatLightbox),
     así que aquí basta con position:fixed normal -a diferencia de
     #asBaGallery, no hace falta reparentarlo por JS-. Oculto por defecto
     (display:none) y solo visible con la clase "is-open" que pone
     project-categories.js. */
  .as-cat-lightbox{
    display:none;
    position:fixed;
    inset:0;
    z-index:4000;
    background:#000;
    flex-direction:column;
    overflow-y:auto;
    -webkit-overflow-scrolling:touch;
    padding:56px 20px 28px;
    box-sizing:border-box;
    opacity:0;
    transform:scale(0.94);
    transition:opacity .48s cubic-bezier(.16,1,.3,1), transform .48s cubic-bezier(.16,1,.3,1);
  }
  .as-cat-lightbox.is-open{ display:flex; }
  /* project-categories.js añade "is-visible" un instante después de
     "is-open" -truco necesario porque display:none no se puede
     transicionar: primero se pasa a flex (invisible), y ya en el
     siguiente frame se anima a opacidad 1 y escala normal, para que la
     entrada se vea suave en vez de aparecer de golpe-. Al cerrar se
     hace lo mismo al revés: se quita "is-visible" (se desvanece) y solo
     cuando termina esa transición se quita "is-open" (vuelve a
     display:none). */
  .as-cat-lightbox.is-open.is-visible{ opacity:1; transform:scale(1); }
  @media (prefers-reduced-motion:reduce){
    .as-cat-lightbox{ transition-duration:.01s; }
  }
  body.as-cat-lightbox-open{ overflow:hidden; }
  .as-cat-lightbox-close{
    position:fixed;
    top:max(14px, env(safe-area-inset-top));
    right:max(14px, env(safe-area-inset-right));
    z-index:4002;
    width:40px;
    height:40px;
    border:none;
    border-radius:50%;
    background:rgba(255,255,255,0.12);
    color:#fff;
    display:flex;
    align-items:center;
    justify-content:center;
    cursor:pointer;
    padding:0;
    touch-action:manipulation;
    -webkit-tap-highlight-color:transparent;
    transition:background .2s ease, transform .2s ease;
  }
  .as-cat-lightbox-close:hover{ background:rgba(255,255,255,0.22); }
  .as-cat-lightbox-close:active{ transform:scale(0.9); }
  .as-cat-lightbox-title{
    margin:0 0 20px;
    font-family:'Anton','Fraunces','Georgia',serif;
    font-weight:400;
    font-style:normal;
    text-transform:uppercase;
    letter-spacing:0.01em;
    font-size:clamp(2.2rem, 9vw, 4.5rem);
    color:#f4efe6;
    text-align:center;
  }
  .as-cat-lightbox-more{
    position:fixed;
    top:max(14px, env(safe-area-inset-top));
    left:max(14px, env(safe-area-inset-left));
    z-index:4002;
    margin:0;
    padding:9px 18px;
    border-radius:999px;
    border:1px solid rgba(255,255,255,0.35);
    color:#fff;
    font-size:0.85rem;
    font-weight:500;
    text-decoration:none;
    white-space:nowrap;
    transition:background .2s ease, border-color .2s ease;
  }
  .as-cat-lightbox-more:hover{
    background:rgba(255,255,255,0.1);
    border-color:rgba(255,255,255,0.55);
  }
  /* Mosaico tipo "muro de fotos": columnas (column-count), no grid con
     recorte. Cada foto conserva su forma real (ni se deforma ni se
     recorta) porque solo se le fija el ancho, el alto es el que le
     corresponda según su proporción -al revés que el intento anterior,
     que forzaba un aspect-ratio fijo con object-fit:cover y acababa
     recortando mal fotos que no eran justo esa proporción-. TODAS las
     fotos, incluida la 1ª, se reparten igual por columnas (antes la 1ª
     foto llevaba column-span:all y salía como banner a todo el ancho,
     mucho más grande que el resto; ahora cae en la columna más corta en
     cada momento, igual que las demás -así es como funciona
     column-count de forma nativa, sin necesitar JS-).
     Mismo número de columnas (3) en TODOS los tamaños de pantalla: en
     móvil se ve exactamente la misma estructura de mosaico que en
     escritorio, solo que a menor tamaño -column-gap y el margen entre
     fotos se van reduciendo en los media queries de abajo para que el
     mosaico completo quepa cómodo en una pantalla estrecha-. */
  .as-cat-lightbox-grid{
    column-count:3;
    column-gap:12px;
    max-width:1400px;
    width:100%;
    margin:0 auto;
  }
  .as-cat-lightbox-grid img{
    display:block;
    width:100%;
    height:auto;
    margin:0 0 12px;
    border-radius:4px;
    background:rgba(255,255,255,0.06);
    break-inside:avoid;
    cursor:zoom-in;
    transition:opacity .2s ease;
  }
  .as-cat-lightbox-grid img:hover{ opacity:0.88; }
  .as-cat-lightbox-empty{
    color:rgba(255,255,255,0.6);
    font-size:0.85rem;
    text-align:center;
    margin:0 auto;
  }

  /* ---------- Visor de una sola foto a pantalla completa ----------
     Se abre AL PULSAR una foto del mosaico de arriba (#asCatLightboxGrid),
     por encima del propio mosaico (z-index más alto que #asCatLightbox,
     que ya está en 4000). Mismo patrón de fundido+escala que el mosaico
     (is-open pasa a display:flex, is-visible anima la entrada un frame
     después -ver comentario de .as-cat-lightbox más arriba-). */
  .as-photo-viewer{
    display:none;
    position:fixed;
    inset:0;
    z-index:4100;
    background:rgba(0,0,0,0.92);
    align-items:center;
    justify-content:center;
    padding:56px 20px;
    box-sizing:border-box;
    opacity:0;
    transform:scale(0.96);
    transition:opacity .32s cubic-bezier(.16,1,.3,1), transform .32s cubic-bezier(.16,1,.3,1);
    cursor:zoom-out;
  }
  .as-photo-viewer.is-open{ display:flex; }
  .as-photo-viewer.is-open.is-visible{ opacity:1; transform:scale(1); }
  @media (prefers-reduced-motion:reduce){
    .as-photo-viewer{ transition-duration:.01s; }
  }
  .as-photo-viewer-img{
    display:block;
    max-width:100%;
    max-height:100%;
    width:auto;
    height:auto;
    border-radius:4px;
    cursor:default;
    box-shadow:0 20px 60px rgba(0,0,0,0.5);
  }
  .as-photo-viewer-close{
    position:fixed;
    top:max(14px, env(safe-area-inset-top));
    right:max(14px, env(safe-area-inset-right));
    z-index:4102;
    width:40px;
    height:40px;
    border:none;
    border-radius:50%;
    background:rgba(255,255,255,0.12);
    color:#fff;
    display:flex;
    align-items:center;
    justify-content:center;
    cursor:pointer;
    padding:0;
    touch-action:manipulation;
    -webkit-tap-highlight-color:transparent;
    transition:background .2s ease, transform .2s ease;
  }
  .as-photo-viewer-close:hover{ background:rgba(255,255,255,0.22); }
  .as-photo-viewer-close:active{ transform:scale(0.9); }
  /* Spinner discreto, esquina inferior, mientras se ve ya la miniatura
     de sobra pero la versión grande todavía está llegando de fondo (ver
     .is-loading, que pone/quita openPhotoViewer en project-categories.js).
     No se pone encima de la foto -ahí ya hay algo que ver, la miniatura-,
     solo como pista discreta de que va a mejorar la nitidez en breve. */
  .as-photo-viewer-spinner{
    display:none;
    position:fixed;
    bottom:max(18px, env(safe-area-inset-bottom));
    right:max(18px, env(safe-area-inset-right));
    z-index:4102;
    width:22px;
    height:22px;
    border-radius:50%;
    border:2.5px solid rgba(255,255,255,0.25);
    border-top-color:#fff;
    animation:asPhotoViewerSpin .8s linear infinite;
  }
  .as-photo-viewer.is-loading .as-photo-viewer-spinner{ display:block; }
  @keyframes asPhotoViewerSpin{ to{ transform:rotate(360deg); } }
  @media (prefers-reduced-motion:reduce){
    .as-photo-viewer-spinner{ animation:none; }
  }
  @media (max-width:900px){
    .as-photo-viewer{ padding:48px 12px; }
  }
  @media (max-width:480px){
    .as-photo-viewer{ padding:40px 8px; }
  }
  /* Pantallas medianas (tablets/móviles grandes): se mantiene la misma
     estructura de 3 columnas, solo con menos aire entre fotos. */
  @media (max-width:900px){
    .as-cat-lightbox{ padding:52px 14px 24px; }
    .as-cat-lightbox-grid{ column-gap:9px; }
    .as-cat-lightbox-grid img{ margin-bottom:9px; border-radius:3px; }
  }
  /* Móvil estrecho: sigue siendo el mismo mosaico de 3 columnas -no se
     colapsa a 1 sola columna, que es lo que rompía la estructura-, solo
     que las fotos se ven más pequeñas todavía, con muy poco espacio
     entre ellas para aprovechar bien el ancho disponible. */
  @media (max-width:480px){
    .as-cat-lightbox{ padding:48px 10px 20px; }
    .as-cat-lightbox-grid{ column-gap:6px; }
    .as-cat-lightbox-grid img{ margin-bottom:6px; border-radius:3px; }
  }


  /* ---------- "Descubre mi trabajo": fila de categorías ---------- */
  .as-discover{ width:100%; }
  .as-discover-eyebrow{
    margin:0;
    font-size:clamp(0.6rem, 1.5vw, 0.72rem);
    font-weight:600;
    letter-spacing:0.2em;
    text-transform:uppercase;
    color:rgba(17,17,17,0.72);
  }
  .as-discover-rule{
    width:32px;
    height:2px;
    border-radius:1px;
    background:rgba(17,17,17,0.2);
    margin:8px auto 14px;
  }
  /* ---------- Título + botones de categorías de "Proyectos" ----------
     A diferencia del resto de #proyectosHeader (que cuelga cerca del
     título "Proyectos", abajo), este bloque se saca arriba del todo con
     su propio position:fixed -mismo top/left que usa el botón de menú,
     pero a la izquierda-, aunque sigue viviendo dentro de
     #proyectosHeader para heredar su misma visibilidad (ver JS en
     scroll-engine.js: pointer-events y opacity se controlan siempre
     desde el ancestro; aquí solo se le añade pointer-events:auto para
     poder tocarlo cuando sí es visible, igual que ya hacía .as-expand). */
  .as-cat-top{
    position:fixed;
    top:44px;
    left:22px;
    z-index:25;
    display:flex;
    flex-direction:column;
    align-items:flex-start;
    gap:8px;
    /* BUGFIX: antes tenía pointer-events:auto siempre, sin condición.
       Al ser position:fixed con coordenadas propias (top/left), ese
       "auto" incondicional lo dejaba clicable en esa misma esquina de
       la pantalla en CUALQUIER otra posición del scroll (Fotos, Mis
       ediciones, etc.), aunque estuviera invisible ahí por el
       opacity:0 heredado de #proyectosHeader -pointer-events no se
       "hereda bloqueado" desde el ancestro: un hijo puede reactivarlo
       igualmente aunque el padre lo tenga en none-. Ahora solo se
       reactiva cuando el ancestro está realmente visible (clase
       "visible", la misma que ya sincroniza scroll-engine.js). */
    pointer-events:none;
  }
  .proyectos-header.visible .as-cat-top{ pointer-events:auto; }
  .as-cat-top-title{
    margin:0;
    font-family:'Fraunces','Georgia',serif;
    font-weight:600;
    font-style:italic;
    font-size:clamp(1.15rem, 3.4vw, 1.6rem);
    letter-spacing:0.02em;
    color:#111111;
    text-shadow:0 1px 3px rgba(255,255,255,0.35), 0 2px 14px rgba(255,255,255,0.25);
  }
  /* Sin categorías creadas todavía, no tiene sentido mostrar el título
     "Fotos" solo, sin ningún botón debajo -lo oculta project-categories.js
     poniendo display:none en todo #asCatTop en ese caso. */
  @media (max-width:480px){
    .as-cat-top{ top:36px; left:18px; }
    .as-cat-top-title{ font-size:clamp(1rem, 4.6vw, 1.3rem); }
  }
  /* ---------- Escritorio (≥901px): "Fotos" + sus botones dejan de
     tener su propio position:fixed independiente y pasan a vivir en el
     flujo normal DENTRO de #proyectosHeader, justo encima de
     "Proyectos" -mismo margen izquierdo que el resto del bloque, así
     queda todo alineado y estructurado como una sola columna-. En
     móvil sigue exactamente igual que antes (fixed, arriba a la
     izquierda). ---------- */
  @media (min-width:901px){
    .as-cat-top{
      position:static;
      top:auto;
      left:auto;
      margin:0 0 clamp(10px, 2vh, 20px);
    }
    .as-cat-top-title{ font-size:clamp(1.8rem, 2.6vw, 2.4rem); }
    .as-cat-btn{ font-size:0.72rem; padding:9px 18px; }
    .as-cat-buttons{ max-width:min(460px, 40vw); }
  }

  /* ---------- Botón "Vídeos de YouTube" (entre "Ediciones" y
     "Proyectos") ----------
     Tarjeta-botón horizontal: miniatura pequeña a la izquierda (la
     portada del primer vídeo, ver proyectos-yt-btn-thumb más abajo y su
     JS en youtube-videos.js), título+subtítulo en el centro y una
     flecha a la derecha. Mismo lenguaje neumórfico que .as-cat-btn
     (sombra doble suave) pero en formato tarjeta ancha, para que
     destaque como su propio "apartado" y no como una píldora más de
     categoría. */
  .proyectos-yt-btn{
    --as-cat-surface:#eef1f5;
    display:flex;
    align-items:center;
    gap:12px;
    width:100%;
    max-width:min(380px, calc(100vw - 60px));
    margin:4px 0 clamp(10px, 2vh, 20px);
    padding:10px 16px 10px 10px;
    border:none;
    border-radius:16px;
    background:var(--as-cat-surface);
    box-shadow:6px 6px 14px rgba(0,0,0,0.10), -5px -5px 12px rgba(255,255,255,0.7);
    cursor:pointer;
    text-align:left;
    pointer-events:none; /* reactivado solo cuando el ancestro está "visible", igual que .as-cat-top/.as-expand */
    transition:box-shadow .18s ease, transform .18s ease;
  }
  .proyectos-header.visible .proyectos-yt-btn{ pointer-events:auto; }
  .proyectos-yt-btn:hover{ transform:translateY(-1px); }
  .proyectos-yt-btn:active{
    transform:translateY(0) scale(0.98);
    box-shadow:inset 4px 4px 10px rgba(0,0,0,0.12), inset -3px -3px 8px rgba(255,255,255,0.7);
  }
  .proyectos-yt-btn-thumb{
    flex:none;
    width:48px;
    height:48px;
    border-radius:12px;
    background-color:#dfe2e6;
    background-size:cover;
    background-position:center;
    display:flex;
    align-items:center;
    justify-content:center;
    color:#4a4d4f;
    overflow:hidden;
  }
  /* Icono "play" de repuesto (mientras no hay miniatura real todavía o
     no hay ningún vídeo guardado): se oculta solo con CSS en cuanto la
     miniatura de fondo se aplica por JS, sin tocar el marcado. */
  .proyectos-yt-btn-thumb[style*="background-image"] svg{ display:none; }
  .proyectos-yt-btn-text{
    display:flex;
    flex-direction:column;
    gap:2px;
    min-width:0;
    flex:1;
  }
  .proyectos-yt-btn-title{
    font-family:'Fraunces','Georgia',serif;
    font-weight:600;
    font-size:0.92rem;
    color:#111111;
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
  }
  .proyectos-yt-btn-sub{
    font-size:0.72rem;
    color:#6b6f73;
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
  }
  .proyectos-yt-btn-arrow{
    flex:none;
    color:var(--accent);
    transition:transform .18s ease;
  }
  .proyectos-yt-btn:hover .proyectos-yt-btn-arrow{ transform:translateX(3px); }
  @media (min-width:901px){
    .proyectos-yt-btn{ max-width:min(460px, 40vw); }
  }

  /* ---------- Botones de categorías de "Proyectos" (Deportes, Fiestas...)
     ----------
     Fila de píldoras pequeñas. Cada una es una categoría creada desde
     Ajustes → Proyectos. Si no hay ninguna, el JS deja todo #asCatTop
     (título incluido) oculto. */
  .as-cat-buttons{
    display:flex;
    flex-wrap:wrap;
    row-gap:10px;
    column-gap:10px;
    max-width:min(380px, calc(100vw - 60px));
  }
  /* Píldoras neumórficas (soft UI), fieles al vídeo de referencia:
     superficie MATE de un solo color (nada de cristal/blur, que
     deslavaba la sombra y no dejaba ver el relieve) + sombra doble
     grande y suave (una oscura abajo-derecha + una clara arriba-
     izquierda) típica del neumorfismo. Al pasar el ratón/tocar y en la
     categoría activa, la sombra se invierte hacia dentro (inset), como
     si el botón se "hundiera" al pulsarlo -mismo patrón que
     div.circle:hover:before del vídeo-. El color de superficie
     (--as-cat-surface) es un gris muy claro, casi plano, para que las
     dos sombras (clara/oscura) se noten con fuerza como en el vídeo;
     lo reutiliza también el efecto de "burbuja" al abrir la galería
     (ver .as-cat-morph). Tamaño pequeño y compacto a propósito -varias
     píldoras cortas se leen y distribuyen mejor en varias líneas que
     pocas píldoras grandes-. */
  .as-cat-btn{
    --as-cat-surface:#eef1f5;
    flex:none;
    position:relative;
    border:none;
    background:var(--as-cat-surface);
    color:#3a3f47;
    font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
    font-size:0.68rem;
    font-weight:700;
    text-transform:uppercase;
    letter-spacing:0.06em;
    padding:8px 16px;
    border-radius:999px;
    cursor:pointer;
    touch-action:manipulation;
    -webkit-tap-highlight-color:transparent;
    box-shadow:
      5px 5px 11px rgba(17,24,39,0.26),
      -5px -5px 11px rgba(255,255,255,0.9);
    transition:color .25s ease, box-shadow .3s ease, transform .18s ease;
  }
  .as-cat-btn:hover{
    color:var(--accent);
    transform:scale(1.05);
    box-shadow:
      6px 6px 13px rgba(17,24,39,0.28),
      -6px -6px 13px rgba(255,255,255,0.95);
  }
  .as-cat-btn:active{
    color:var(--accent);
    transform:scale(0.95);
    box-shadow:
      inset 3px 3px 8px rgba(17,24,39,0.28),
      inset -3px -3px 8px rgba(255,255,255,0.85);
  }
  /* Categoría activa (la que tiene su galería abierta ahora mismo):
     se queda "hundida" (inset) y en color de acento aunque el ratón no
     esté encima, para que se note cuál es sin depender del hover
     -project-categories.js le pone/quita la clase is-active al abrir/
     cerrar cada galería-. */
  .as-cat-btn.is-active{
    color:var(--accent);
    box-shadow:
      inset 3px 3px 8px rgba(17,24,39,0.28),
      inset -3px -3px 8px rgba(255,255,255,0.85);
  }

  /* ---------- Efecto al pulsar una categoría: el BOTÓN se "implosiona"
     (se encoge sobre sí mismo y desaparece), sin que el resto de la
     página se mueva ni haga zoom en ningún momento -a diferencia de una
     burbuja que crece cubriendo toda la pantalla, aquí la animación se
     queda contenida en el propio botón-. La galería aparece justo
     después con su propio fundido+escala suave (ver
     .as-cat-lightbox.is-visible más arriba). Pequeño "squish" hacia
     fuera antes de encogerse (35%) para que se sienta vivo y no
     mecánico. */
  @keyframes asCatImplode{
    0%{ transform:scale(1); opacity:1; }
    35%{ transform:scale(1.12); opacity:1; }
    100%{ transform:scale(0); opacity:0; }
  }
  .as-cat-btn.is-launching{
    animation:asCatImplode .42s cubic-bezier(.4,0,.2,1) forwards;
    pointer-events:none;
  }
  @media (prefers-reduced-motion:reduce){
    .as-cat-btn.is-launching{ animation-duration:.01s; }
  }

  /* ---------- Galería que se expande (tarjetas estrechas -> foto grande
     con texto al pasar el ratón / tocar) ----------
     Fila flex de tarjetas iguales (flex-grow:1) con overflow:hidden y
     foto de fondo. Al pasar el ratón (o con la clase .is-active que pone
     el JS al tocar en móvil), la tarjeta activa sube mucho su flex-grow
     y, gracias a :has(), las demás bajan el suyo por debajo de 1 -no se
     quedan solo "cediendo espacio", se encogen de verdad a una tira
     fina-. La franja oscura y el texto (nombre + frase) están siempre en
     el DOM pero con opacity:0 hasta que la tarjeta se expande, para que
     la aparición sea un fundido suave y no un salto brusco. */
  /* Antes el tamaño de esta fila dependía del ancho/alto de la pantalla
     (vw/vh), así que la misma foto se veía "más o menos ampliada" según
     el dispositivo (móvil, tablet, ordenador...): en unos se veía bien y
     en otros raro, sin forma de saber de antemano cómo iba a quedar.
     Ahora el tamaño es FIJO en píxeles -el mismo en cualquier pantalla-,
     así el recorte de cada foto es exactamente igual en todos los
     dispositivos. Con min(360px, 92vw) solo se encoge en los móviles más
     estrechos que 391px (391*0.92≈360), lo justo para no desbordar. */
  /* BUGFIX: mismo problema que .as-cat-top de arriba -pointer-events:auto
     incondicional sobre un bloque que cuelga siempre de #proyectosHeader
     (position:fixed en móvil vía display:contents del padre)-: quedaba
     clicable en su rincón de pantalla aunque #proyectosHeader estuviera
     invisible en otra posición del scroll. Se gatilla igual que
     .as-cat-top, solo con el ancestro visible. */
  .as-expand{ width:min(360px, 92vw); margin-top:clamp(4px, 1vh, 12px); pointer-events:none; }
  .proyectos-header.visible .as-expand{ pointer-events:auto; }
  .as-expand-grid{
    display:flex;
    gap:8px;
    width:100%;
    height:160px;
  }
  .as-expand-card{
    position:relative;
    flex:1 1 0%;
    min-width:0;
    border-radius:16px;
    overflow:hidden;
    cursor:pointer;
    /* Color de respaldo (sin foto, o mientras se ve el margen del modo
       "Ajustar"): mismo tono oscuro del panel de detrás, para que en modo
       "Ajustar" (sin recortar) el margen que deja la foto se vea limpio
       en vez de blanco. La foto en sí ya no va aquí -ver
       .as-expand-card-photo justo debajo-, así el zoom de la foto nunca
       deforma este recuadro ni su borde redondeado. */
    background-color:#ffffff;
    border:1px solid rgba(17,17,17,0.1);
    transition:flex-grow .55s cubic-bezier(.22,1,.36,1);
  }
  /* Capa de la foto en sí, dentro del recuadro fijo de arriba: ocupa
     TODO el recuadro (inset:0) y es la única parte que responde al
     zoom (transform:scale, ver expandCardPhotoStyle en
     comparison-pairs.js) -el recuadro que la contiene nunca cambia de
     tamaño ni de forma, solo recorta con overflow:hidden lo que sobra al
     acercar la foto-. "auto 100%" en vez de "cover": así la foto se
     escala SIEMPRE al mismo alto fijo de la tarjeta -que no cambia ni al
     expandirse una tarjeta ni entre dispositivos-, en vez de recalcular
     su zoom cada vez que cambia el ancho del recuadro. */
  .as-expand-card-photo{
    position:absolute;
    inset:0;
    background-size:auto 100%;
    background-position:center;
    background-repeat:no-repeat;
  }
  /* IMPORTANTE: la tarjeta que se abre es SIEMPRE la que lleva la clase
     .is-active, puesta por JS (paso automático o al pasar el ratón/tocar
     -ver el listener "mouseenter" y el "click" más abajo-). Ya no se usa
     :hover en el CSS: si se dejaba el :hover suelto, dos tarjetas podían
     "pelearse" por expandirse a la vez (la que llevaba is-active y la que
     el ratón tocaba de refilón) y, durante ese conflicto, se veía toda la
     fila con las tarjetas del mismo ancho -exactamente el fallo de "se
     ponen todas igual" que no queremos-. Con una única fuente de verdad
     (is-active) siempre hay una y solo una tarjeta abierta. */
  .as-expand-card.is-active{
    flex-grow:9;
  }
  /* En cuanto una tarjeta de la fila está activa, todas las demás bajan su
     flex-grow a una fracción pequeña -así se estrechan mucho más de lo
     que crece la activa, en vez de repartirse el espacio a partes
     iguales-. */
  .as-expand-grid:has(.as-expand-card.is-active) .as-expand-card:not(.is-active){
    flex-grow:0.35;
  }
  .as-expand-card-shade{
    position:absolute;
    inset:auto 0 0 0;
    height:65%;
    background:linear-gradient(to top, rgba(0,0,0,0.82), rgba(0,0,0,0) 100%);
    opacity:0;
    transition:opacity .4s ease;
  }
  .as-expand-card.is-active .as-expand-card-shade{
    opacity:1;
  }
  .as-expand-card-info{
    position:absolute;
    left:16px;
    right:16px;
    bottom:14px;
    color:#f5f5f5;
    opacity:0;
    transform:translateY(10px);
    transition:opacity .3s ease, transform .3s ease;
    white-space:nowrap;
    overflow:hidden;
  }
  .as-expand-card.is-active .as-expand-card-info{
    opacity:1;
    transform:translateY(0);
    transition-delay:.12s;
  }
  .as-expand-card-label{
    margin:0;
    font-size:0.86rem;
    font-weight:600;
    letter-spacing:0.01em;
  }
  .as-expand-card-phrase{
    margin:2px 0 0;
    font-size:0.72rem;
    color:rgba(245,245,245,0.75);
  }
  .as-expand-card-dot{
    position:absolute;
    top:12px;
    left:12px;
    width:8px;
    height:8px;
    border-radius:50%;
    background:var(--accent);
    opacity:0;
    transform:scale(0.4);
    transition:opacity .3s ease, transform .3s ease;
  }
  .as-expand-card.is-active .as-expand-card-dot{
    opacity:1;
    transform:scale(1);
    transition-delay:.12s;
  }
  /* Ya no hace falta ningún ajuste especial por debajo de 520px: el
     tamaño de la fila es fijo (ver .as-expand más arriba) y solo se
     encoge un poco vía min(360px, 92vw) en los móviles más estrechos,
     nunca cambia de alto ni oculta la frase. */

  /* Nombres de las dos cámaras grandes de la foto (no las miniaturas del
     carrusel de abajo). ANTES se colocaban a yFrac:0.40 del recuadro de la
     foto, una altura que -según el propio fotograma final- cae DENTRO del
     cuerpo de las cámaras (que ocupan aprox. 0.35-0.62 de esa altura), así
     que el texto quedaba montado encima del objeto. AHORA la etiqueta se
     ancla claramente por encima (ver CAMERA_LABEL_POS en el JS, yFrac más
     bajo) y además vive dentro de una "chip" (fondo translúcido + blur +
     borde fino) en vez de texto suelto: así queda legible pase lo que pase
     debajo, y con una pequeña línea+punto conector hacia la cámara se lee
     claramente como una etiqueta de producto bien estructurada, no como un
     rótulo flotando sin relación con lo que señala. */
  .camera-name-label{
    position:fixed;
    z-index:30;
    margin:0;
    display:inline-flex;
    align-items:center;
    /* -100% sube el bloque entero por encima del punto de anclaje; los 16px
       extra son el aire de seguridad entre la chip y la cámara, y el hueco
       para el conector (línea + punto) que se dibuja debajo con ::after. */
    transform:translate(-50%, calc(-100% - 16px - var(--pe, 0px)));
    padding:7px 16px;
    border-radius:999px;
    background:rgba(255,255,255,0.78);
    -webkit-backdrop-filter:blur(8px) saturate(1.4);
    backdrop-filter:blur(8px) saturate(1.4);
    border:1px solid rgba(17,17,17,0.08);
    box-shadow:0 6px 18px rgba(17,17,17,0.07);
    font-family:'Fraunces','Georgia',serif;
    font-weight:600;
    font-size:clamp(0.72rem, 1.9vw, 0.95rem);
    letter-spacing:0.04em;
    text-transform:uppercase;
    color:var(--fg);
    text-align:center;
    white-space:nowrap;
    opacity:0;
    pointer-events:none;
    transition:opacity .3s ease;
  }
  /* Conector: una línea corta y un punto naranja que bajan desde la chip
     hasta el punto exacto de anclaje (justo encima de la cámara), para que
     quede claro a qué cámara se refiere cada etiqueta sin necesidad de
     superponer el texto sobre la propia pieza. */
  .camera-name-label::after{
    content:'';
    position:absolute;
    left:50%;
    top:100%;
    width:1px;
    height:12px;
    background:linear-gradient(to bottom, rgba(17,17,17,0.22), rgba(17,17,17,0));
    transform:translateX(-50%);
  }
  .camera-name-label::before{
    content:'';
    position:absolute;
    left:50%;
    top:calc(100% + 11px);
    width:5px;
    height:5px;
    border-radius:50%;
    background:var(--accent);
    transform:translate(-50%, -50%);
    box-shadow:0 0 0 3px rgba(255,90,31,0.16);
  }
  .camera-name-label.visible{ opacity:1; }
  @media (max-width:480px){
    .camera-name-label{ font-size:0.66rem; letter-spacing:0.03em; padding:6px 12px; }
  }
  @media (max-width:340px){
    .camera-name-label{ font-size:0.6rem; padding:5px 10px; }
  }
  /* Con poca altura de pantalla (móvil en horizontal) se acorta el hueco
     entre la chip y la cámara, para no perder espacio vertical. */
  @media (max-height:520px){
    .camera-name-label{ transform:translate(-50%, calc(-100% - 10px - var(--pe, 0px))); }
    .camera-name-label::after{ height:7px; }
    .camera-name-label::before{ top:calc(100% + 6px); }
  }

  /* ---------- Carrusel circular de cámaras (aparece en el último fotograma) ---------- */
  .camera-carousel{
    position:fixed;
    left:50%;
    bottom:6%;
    transform:translateX(-50%) translateY(14px);
    /* Ancho de arranque antes de que el JS lo recalcule (sizeCarousel);
       el JS lo sobreescribe siempre con el ancho exacto de 3 fotos, para
       que nunca se vea un cuarto hueco/foto asomando por el lateral. */
    width:min(92vw, 640px);
    max-width:100%;
    z-index:30;
    opacity:0;
    pointer-events:none;
    transition:opacity .3s ease, transform .3s ease;
  }
  .camera-carousel.visible{
    opacity:1;
    /* --pe (post-end): desplazamiento fluido en px que añade el scroll real
       tras la última parada; lo fija el JS y por defecto es 0 (sin cambios
       de comportamiento mientras no se haga scroll más allá del final). */
    transform:translateX(-50%) translateY(calc(0px - var(--pe, 0px)));
  }
  /* La cinta se mueve sola sin parar hacia la izquierda; el viewport recorta
     el sobrante y, con la máscara en degradado, las fotos se van
     desvaneciendo al salir por la izquierda y aparecen igual de suaves por
     la derecha, en vez de cortarse de golpe en el borde.
     IMPORTANTE: el ancho de .camera-carousel lo fija el JS para que quepan
     EXACTAMENTE 3 fotos (ver sizeCarousel()). La zona de difuminado ocupa
     ahora buena parte de cada lado (aprox. una foto entera a cada extremo),
     así la foto que entra o sale nunca aparece "de golpe" con un borde
     recto: solo la del centro se ve siempre nítida al 100%, y las de los
     lados entran/salen ya semi-transparentes, disolviéndose en el fondo
     en vez de cortarse en una línea blanca recta. */
  .camera-carousel .marquee-viewport{
    position:relative;
    overflow:hidden;
    width:100%;
    /* Sin fondo: las fotos flotan directamente sobre la escena, sin
       ningún rectángulo de color detrás. El difuminado en los bordes (para
       que la foto que entra o sale no aparezca de golpe) ahora se hace con
       una máscara de opacidad sobre el propio contenido, no pintando un
       degradado de color por encima; así nunca se ve ningún bloque ni
       borde recto, solo las fotos apareciendo/desvaneciéndose. */
    -webkit-mask-image:linear-gradient(to right, transparent 0%, #000 15%, #000 85%, transparent 100%);
    mask-image:linear-gradient(to right, transparent 0%, #000 15%, #000 85%, transparent 100%);
  }
  .camera-carousel .marquee-track{
    display:flex;
    align-items:flex-start;
    width:max-content;
    animation:cameraMarquee linear infinite;
    animation-duration:16s; /* se recalcula por JS según el nº de fotos */
  }
  .camera-carousel.visible .marquee-track{
    animation-play-state:running;
  }
  .camera-carousel .marquee-track{
    animation-play-state:paused; /* arranca solo cuando el carrusel es visible */
  }
  @keyframes cameraMarquee{
    from{ transform:translateX(0); }
    to{ transform:translateX(-50%); }
  }
  /* Cada "celda" del carrusel: la foto y, debajo, su nombre (si el usuario
     le puso uno en Ajustes). El margen que antes llevaba la foto ahora
     vive aquí, en el contenedor, para que foto + texto se muevan siempre
     juntos como un bloque. */
  .camera-carousel .marquee-item{
    flex:0 0 auto;
    margin:0 12px;
    display:flex;
    flex-direction:column;
    align-items:center;
  }
  .camera-carousel .marquee-slide{
    flex:0 0 auto;
    background-position:center;
    background-size:cover;
    /* Sin color de fondo ni sombra: así no se ve ningún rectángulo, solo
       la foto en sí, recortada con esquinas suaves. */
    background-color:transparent;
    /* Fluido en vez de un salto brusco entre un tamaño fijo de escritorio
       (120px) y otro de móvil (88px) en un único punto de corte: así se
       ve proporcionado en cualquier ancho intermedio (tablets) también. */
    width:clamp(80px, 15vw, 128px);
    height:clamp(80px, 15vw, 128px);
    border-radius:12px;
    filter:brightness(0.92);
  }
  /* Nombre de la foto, centrado justo debajo. Solo se pinta cuando el
     usuario ha escrito algo en Ajustes (si no, no se genera el elemento y
     no queda hueco vacío reservado). Tipografía a juego con el resto del
     texto elegante de la web (Fraunces), en tamaño discreto para no
     competir visualmente con las fotos. Se recorta con ellipsis si el
     nombre es muy largo, así nunca rompe el ancho fijo de la celda ni
     empuja a las fotos vecinas. */
  /* Nombre de la foto, centrado justo debajo. Solo se pinta cuando el
     usuario ha escrito algo en Ajustes (si no, no se genera el elemento y
     no queda hueco vacío reservado). Tipografía a juego con el resto del
     texto elegante de la web (Fraunces), en tamaño discreto para no
     competir visualmente con las fotos. Si el nombre es largo, hace
     salto de línea (nunca se corta con puntos suspensivos); como cada
     celda del carrusel es una columna independiente con alto automático,
     que el texto ocupe dos o tres líneas no descoloca a las fotos
     vecinas. */
  .camera-carousel .marquee-caption{
    margin-top:8px;
    /* Mismo ancho fluido que la foto (clamp), para que el texto nunca
       quede más ancho ni más estrecho que la imagen que describe. */
    max-width:clamp(80px, 15vw, 128px);
    font-family:'Fraunces','Georgia',serif;
    font-weight:500;
    font-size:clamp(0.68rem, 1.6vw, 0.78rem);
    letter-spacing:0.01em;
    line-height:1.3;
    text-align:center;
    white-space:normal;
    overflow-wrap:break-word;
    word-break:break-word;
    /* Letra en negro (--fg) a petición: sin círculo/chip de fondo, solo el
       texto en sí. Como el carrusel aparece sobre el degradado OSCURO del
       último fotograma, un negro puro sin más ahí sería casi invisible;
       por eso se le añade un halo claro (text-shadow blanco) que hace de
       "borde" de contraste contra ese fondo oscuro, igual que antes el
       texto blanco llevaba un halo oscuro contra el blanco. */
    color:var(--fg);
    text-shadow:
      0 1px 2px rgba(255,255,255,0.75),
      0 0 6px rgba(255,255,255,0.55);
  }
  /* Sombra suave detrás de cada foto para que su borde no se funda con el
     fondo oscurecido justo debajo, sin necesidad de ponerle un recuadro de
     color fijo. */
  .camera-carousel .marquee-slide{
    box-shadow:0 2px 10px rgba(0,0,0,0.22);
  }
  @media (max-width:480px){
    .camera-carousel{ bottom:4%; }
  }
  @media (max-width:340px){
    .camera-carousel{ bottom:3.5%; }
    .camera-carousel .marquee-item{ margin:0 8px; }
  }
  /* Monitores muy anchos: se permite un carrusel algo más ancho (más
     fotos visibles a la vez) para que no quede como una tira diminuta
     perdida en medio de una pantalla enorme. */
  @media (min-width:1600px){
    .camera-carousel{ width:min(70vw, 780px); bottom:7%; }
  }
  /* Pantallas bajas (móvil en horizontal): el carrusel baja su margen
     inferior y las fotos se compactan un poco para que quepa todo el
     bloque (título + frase + carrusel) sin salirse de la pantalla. */
  @media (max-height:520px){
    .camera-carousel{ bottom:3%; }
    .camera-carousel .marquee-slide{ width:clamp(64px, 12vh, 92px); height:clamp(64px, 12vh, 92px); }
    .camera-carousel .marquee-caption{ display:none; }
  }

  /* ---------- Acceso de administrador (cuenta con contraseña) ----------
     .admin-login-card y las clases de dentro las reutiliza admin.html,
     como página independiente en vez de overlay flotante sobre el sitio. */
  .admin-login-card{
    width:100%;
    max-width:360px;
    background:var(--bg);
    border-radius:18px;
    padding:28px 26px 24px;
    box-shadow:0 20px 60px rgba(0,0,0,0.25);
    animation:ajustesPanelIn .28s ease;
  }
  .admin-login-title{
    font-family:'Fraunces','Georgia',serif;
    font-weight:600;
    font-size:1.3rem;
    margin:0 0 4px;
  }
  .admin-login-sub{
    font-size:0.84rem;
    color:var(--dim);
    margin:0 0 20px;
    line-height:1.4;
  }
  .admin-login-card input[type="password"],
  .admin-login-card input[type="text"]{
    width:100%;
    box-sizing:border-box;
    padding:12px 14px;
    border-radius:10px;
    border:1px solid rgba(0,0,0,0.14);
    font-size:0.95rem;
    font-family:inherit;
    margin-bottom:10px;
  }
  .admin-login-card input:focus{ outline:2px solid var(--accent); outline-offset:1px; }
  .admin-login-actions{
    display:flex;
    gap:10px;
    margin-top:6px;
  }
  .admin-login-actions button{
    flex:1;
    border:none;
    border-radius:999px;
    padding:11px 14px;
    font-family:'Fraunces','Georgia',serif;
    font-weight:500;
    font-size:0.86rem;
    cursor:pointer;
    transition:opacity .2s ease, transform .15s ease;
  }
  .admin-login-primary{ background:var(--accent); color:#fff; }
  .admin-login-primary:hover{ opacity:0.9; }
  .admin-login-error{
    color:#c0392b;
    font-size:0.8rem;
    margin:0 0 10px;
    display:none;
  }
  .admin-login-error.visible{ display:block; }

  /* ---------- Ajustes: pestaña "Cuenta" (historial + copia de seguridad) ---------- */
  .cuenta-status{
    display:flex;
    align-items:center;
    gap:10px;
    padding:12px 14px;
    border-radius:12px;
    background:rgba(0,0,0,0.03);
    font-size:0.86rem;
    color:var(--dim);
    margin-bottom:18px;
  }
  .cuenta-status .dot-live{
    width:8px; height:8px; border-radius:50%; background:#2ecc71; flex:0 0 auto;
  }
  .cuenta-row{
    display:flex;
    flex-wrap:wrap;
    gap:10px;
    margin-bottom:8px;
  }
  .cuenta-btn{
    display:inline-flex;
    align-items:center;
    gap:7px;
    background:#fff;
    border:1px solid rgba(0,0,0,0.14);
    border-radius:999px;
    padding:10px 18px;
    font-family:'Fraunces','Georgia',serif;
    font-weight:500;
    font-size:0.82rem;
    color:var(--fg);
    cursor:pointer;
    transition:border-color .2s ease, color .2s ease, box-shadow .2s ease;
  }
  .cuenta-btn-icon{ flex:none; width:15px; height:15px; }
  .cuenta-btn:hover{ border-color:rgba(255,90,31,0.5); color:var(--accent); box-shadow:0 2px 10px rgba(255,90,31,0.12); }
  .cuenta-btn--danger:hover{ border-color:#c0392b; color:#c0392b; box-shadow:none; }
  .cuenta-form{ margin-top:18px; }
  .cuenta-form input{
    width:100%;
    box-sizing:border-box;
    padding:11px 13px;
    border-radius:10px;
    border:1px solid rgba(0,0,0,0.14);
    font-size:0.9rem;
    font-family:inherit;
    margin-bottom:9px;
  }
  /* Campo de contraseña + botón de ojo (mostrar/ocultar) superpuesto a
     la derecha, dentro del propio input. */
  .cuenta-pass-field{ position:relative; }
  .cuenta-pass-field input{ padding-right:42px; }
  .cuenta-pass-toggle{
    position:absolute;
    top:0;
    right:0;
    bottom:9px;
    width:38px;
    display:flex;
    align-items:center;
    justify-content:center;
    background:none;
    border:none;
    padding:0;
    margin:0;
    cursor:pointer;
    color:var(--dim);
  }
  .cuenta-pass-toggle:hover{ color:var(--accent); }
  /* Icono de ojo/ojo-tachado dibujado en CSS puro (sin depender de JS
     para inyectar SVG): dos capas superpuestas, se alterna cuál se ve
     con la clase .is-visible que pone view-navigation... */
  .cuenta-pass-toggle::before,
  .cuenta-pass-toggle::after{
    content:"";
    position:absolute;
    width:17px;
    height:17px;
    background-repeat:no-repeat;
    background-position:center;
    background-size:contain;
  }
  .cuenta-pass-toggle::before{
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15.58 12C15.58 13.98 13.98 15.58 12 15.58C10.02 15.58 8.42 13.98 8.42 12C8.42 10.02 10.02 8.42 12 8.42C13.98 8.42 15.58 10.02 15.58 12Z'/%3E%3Cpath d='M12 20.27C15.53 20.27 18.82 18.19 21.11 14.59C22.01 13.18 22.01 10.81 21.11 9.4C18.82 5.8 15.53 3.72 12 3.72C8.47 3.72 5.18 5.8 2.89 9.4C1.99 10.81 1.99 13.18 2.89 14.59C5.18 18.19 8.47 20.27 12 20.27Z'/%3E%3C/svg%3E");
    opacity:0.65;
  }
  .cuenta-pass-toggle::after{
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14.53 9.47L9.47 14.53C8.82 13.88 8.42 12.99 8.42 12C8.42 10.02 10.02 8.42 12 8.42C12.99 8.42 13.88 8.82 14.53 9.47Z'/%3E%3Cpath d='M17.82 5.77C16.07 4.45 14.07 3.73 12 3.73C8.47 3.73 5.18 5.81 2.89 9.41C1.99 10.82 1.99 13.19 2.89 14.6C3.68 15.84 4.6 16.91 5.6 17.77'/%3E%3Cpath d='M8.42 19.53C9.56 20.01 10.77 20.27 12 20.27C15.53 20.27 18.82 18.19 21.11 14.59C22.01 13.18 22.01 10.81 21.11 9.4C20.78 8.88 20.42 8.39 20.05 7.93'/%3E%3Cpath d='M15.51 12.7C15.25 14.11 14.1 15.26 12.69 15.52'/%3E%3Cpath d='M9.47 14.53L2 22'/%3E%3Cpath d='M22 2L14.53 9.47'/%3E%3C/svg%3E");
    opacity:0;
  }
  .cuenta-pass-toggle[aria-pressed="true"]::before{ opacity:0; }
  .cuenta-pass-toggle[aria-pressed="true"]::after{ opacity:0.65; }
  .cuenta-msg{
    font-size:0.8rem;
    margin:8px 0 0;
    display:none;
  }
  .cuenta-msg.visible{ display:block; }
  .cuenta-msg--ok{ color:#1e8e5a; }
  .cuenta-msg--err{ color:#c0392b; }
  .historial-list{
    list-style:none;
    margin:0;
    padding:0;
    max-height:340px;
    overflow-y:auto;
    border:1px solid rgba(0,0,0,0.08);
    border-radius:12px;
  }
  .historial-item{
    padding:12px 14px;
    border-bottom:1px solid rgba(0,0,0,0.06);
    font-size:0.84rem;
  }
  .historial-item:last-child{ border-bottom:none; }
  .historial-item-top{
    display:flex;
    justify-content:space-between;
    gap:10px;
    margin-bottom:2px;
  }
  .historial-item-action{ font-weight:600; }
  .historial-item-time{ color:var(--dim); font-size:0.74rem; white-space:nowrap; }
  .historial-item-detail{ color:var(--dim); }
  .historial-empty{ padding:16px; text-align:center; color:var(--dim); font-size:0.84rem; }

  /* =======================================================================
     PANEL DE ADMINISTRADOR — tema propio, deliberadamente distinto del
     resto de la web. La web pública usa serif Fraunces, fondo claro y
     acento naranja; aquí usamos una tipografía de sistema, fondo oscuro
     tipo "panel de control" y acento cian, para que quede clarísimo en
     todo momento que estás en una zona de gestión y no en la web que ve
     el público. Todas las reglas van con prefijo #view-ajustes para no
     tocar ni una coma del resto del sitio.
     ======================================================================= */
  #view-ajustes{
    --adm-bg:#0d1117;
    --adm-surface:#161b25;
    --adm-surface-2:#1d2430;
    --adm-border:rgba(255,255,255,0.09);
    --adm-fg:#e8edf5;
    --adm-dim:#8994a6;
    --adm-accent:#33e6c8;
    --adm-accent-dim:rgba(51,230,200,0.14);
    --adm-danger:#ff6767;
    background:var(--adm-bg) !important;
    color:var(--adm-fg);
    font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  }
  #view-ajustes .subview-inner{
    max-width:960px;
    padding:96px 20px 60px;
  }
  #view-ajustes .subview-eyebrow{
    font-family:inherit;
    font-weight:700;
    letter-spacing:0.18em;
    color:var(--adm-accent);
  }
  #view-ajustes h1{
    font-family:inherit;
    font-weight:800;
    letter-spacing:-0.01em;
    color:var(--adm-fg);
    font-size:clamp(1.5rem, 4vw, 2.1rem);
    display:flex;
    align-items:center;
    gap:12px;
  }
  #view-ajustes h1::before{
    content:"";
    display:inline-block;
    width:10px;
    height:10px;
    border-radius:50%;
    background:var(--adm-accent);
    box-shadow:0 0 0 4px var(--adm-accent-dim);
    flex-shrink:0;
  }
  #view-ajustes p.lead{
    font-family:inherit;
    font-style:normal;
    color:var(--adm-dim);
    font-size:0.95rem;
  }


  /* Barra de pestañas: en vez de la píldora clara de la web, una barra
     oscura tipo consola con la pestaña activa marcada por un acento cian
     inferior, mayúsculas y letras espaciadas — como un panel de control. */
  #view-ajustes .ajustes-tabs{
    background:var(--adm-surface);
    border:1px solid var(--adm-border);
    border-radius:10px;
    padding:4px;
    gap:2px;
  }
  #view-ajustes .ajustes-tab{
    font-family:inherit;
    font-weight:600;
    font-style:normal;
    font-size:0.72rem;
    letter-spacing:0.06em;
    text-transform:uppercase;
    color:var(--adm-dim);
    border-radius:7px;
    padding:10px 14px;
  }
  #view-ajustes .ajustes-tab:hover{ color:var(--adm-fg); background:rgba(255,255,255,0.04); }
  #view-ajustes .ajustes-tab.active{
    color:#04231d;
    font-weight:700;
    background:var(--adm-accent);
    box-shadow:none;
  }

  #view-ajustes .ajustes-section{
    background:var(--adm-surface);
    border:1px solid var(--adm-border);
    border-radius:12px;
    box-shadow:none;
  }
  #view-ajustes .ajustes-section-icon{
    background:var(--adm-accent-dim);
    color:var(--adm-accent);
    border-radius:8px;
    box-shadow:none;
  }
  #view-ajustes .ajustes-section-title{
    font-family:inherit;
    font-weight:700;
    color:var(--adm-fg);
  }
  #view-ajustes .ajustes-section-desc{ color:var(--adm-dim); }

  #view-ajustes .ajustes-note{
    background:var(--adm-surface-2);
    border:1px solid var(--adm-border);
    border-left:3px solid var(--adm-accent);
  }
  #view-ajustes .ajustes-note p{ color:var(--adm-dim); }
  #view-ajustes .ajustes-note p strong{ color:var(--adm-fg); }

  #view-ajustes .ajustes-uploader{
    border:1.5px dashed rgba(255,255,255,0.18);
    background:var(--adm-surface-2);
  }
  #view-ajustes .ajustes-uploader:hover{
    background:var(--adm-accent-dim);
    border-color:var(--adm-accent);
  }
  #view-ajustes .ajustes-uploader-icon{
    background:var(--adm-accent-dim);
    color:var(--adm-accent);
  }
  #view-ajustes .ajustes-uploader .ajustes-uploader-label{
    font-family:inherit;
    font-weight:700;
    color:var(--adm-fg);
  }
  #view-ajustes .ajustes-uploader .ajustes-uploader-sub{ color:var(--adm-dim); }

  #view-ajustes .ajustes-thumb{
    background:var(--adm-surface-2);
    box-shadow:0 4px 16px rgba(0,0,0,0.35);
    border:1px solid var(--adm-border);
  }
  #view-ajustes .ajustes-thumb-name{
    font-family:inherit;
    font-style:normal;
    background:var(--adm-surface-2);
    border:1px solid var(--adm-border);
    color:var(--adm-fg);
  }
  #view-ajustes .ajustes-thumb-name::placeholder{ color:var(--adm-dim); }
  #view-ajustes .ajustes-thumb-name:focus{
    border-color:var(--adm-accent);
    box-shadow:0 0 0 3px var(--adm-accent-dim);
  }
  #view-ajustes .ajustes-thumb button{
    background:rgba(13,17,23,0.85);
    color:var(--adm-fg);
  }
  #view-ajustes .ajustes-thumb button:hover{ background:var(--adm-danger); color:#fff; }
  #view-ajustes .ajustes-empty{ color:var(--adm-dim); font-style:normal; }
  #view-ajustes .ajustes-clear{
    font-family:inherit;
    font-weight:600;
    background:var(--adm-surface-2);
    border:1px solid var(--adm-border);
    color:var(--adm-dim);
  }
  #view-ajustes .ajustes-clear:hover{
    border-color:var(--adm-danger);
    color:var(--adm-danger);
    box-shadow:none;
  }

  /* Campos de texto y botones de las pestañas "Textos" y "Cuenta" */
  #view-ajustes textarea,
  #view-ajustes .cuenta-form input{
    background:var(--adm-surface-2) !important;
    border:1px solid var(--adm-border) !important;
    color:var(--adm-fg) !important;
    font-family:inherit !important;
  }
  #view-ajustes .cuenta-pass-toggle{ color:var(--adm-dim); }
  #view-ajustes .cuenta-pass-toggle:hover{ color:var(--adm-accent); }
  #view-ajustes .cuenta-pass-toggle::before{
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15.58 12C15.58 13.98 13.98 15.58 12 15.58C10.02 15.58 8.42 13.98 8.42 12C8.42 10.02 10.02 8.42 12 8.42C13.98 8.42 15.58 10.02 15.58 12Z'/%3E%3Cpath d='M12 20.27C15.53 20.27 18.82 18.19 21.11 14.59C22.01 13.18 22.01 10.81 21.11 9.4C18.82 5.8 15.53 3.72 12 3.72C8.47 3.72 5.18 5.8 2.89 9.4C1.99 10.81 1.99 13.18 2.89 14.59C5.18 18.19 8.47 20.27 12 20.27Z'/%3E%3C/svg%3E");
  }
  #view-ajustes .cuenta-pass-toggle::after{
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14.53 9.47L9.47 14.53C8.82 13.88 8.42 12.99 8.42 12C8.42 10.02 10.02 8.42 12 8.42C12.99 8.42 13.88 8.82 14.53 9.47Z'/%3E%3Cpath d='M17.82 5.77C16.07 4.45 14.07 3.73 12 3.73C8.47 3.73 5.18 5.81 2.89 9.41C1.99 10.82 1.99 13.19 2.89 14.6C3.68 15.84 4.6 16.91 5.6 17.77'/%3E%3Cpath d='M8.42 19.53C9.56 20.01 10.77 20.27 12 20.27C15.53 20.27 18.82 18.19 21.11 14.59C22.01 13.18 22.01 10.81 21.11 9.4C20.78 8.88 20.42 8.39 20.05 7.93'/%3E%3Cpath d='M15.51 12.7C15.25 14.11 14.1 15.26 12.69 15.52'/%3E%3Cpath d='M9.47 14.53L2 22'/%3E%3Cpath d='M22 2L14.53 9.47'/%3E%3C/svg%3E");
  }
  #view-ajustes textarea:focus,
  #view-ajustes .cuenta-form input:focus{
    outline:none;
    border-color:var(--adm-accent) !important;
    box-shadow:0 0 0 3px var(--adm-accent-dim);
  }
  #view-ajustes textarea::placeholder,
  #view-ajustes .cuenta-form input::placeholder{ color:var(--adm-dim); }
  #view-ajustes .ajustes-section-desc[for]{ color:var(--adm-dim); }

  #view-ajustes .cuenta-status{ color:var(--adm-dim); }
  #view-ajustes .dot-live{
    background:var(--adm-accent);
    box-shadow:0 0 0 4px var(--adm-accent-dim);
  }
  #view-ajustes .cuenta-btn{
    font-family:inherit;
    font-weight:700;
    font-size:0.78rem;
    letter-spacing:0.03em;
    text-transform:uppercase;
    background:var(--adm-surface-2);
    border:1px solid var(--adm-border);
    color:var(--adm-fg);
    border-radius:8px;
  }
  #view-ajustes .cuenta-btn:hover{
    border-color:var(--adm-accent);
    color:var(--adm-accent);
    box-shadow:none;
  }
  #view-ajustes .cuenta-btn--danger{ color:var(--adm-danger); }
  #view-ajustes .cuenta-btn--danger:hover{ border-color:var(--adm-danger); color:var(--adm-danger); }
  #view-ajustes .cuenta-msg--ok{ color:var(--adm-accent); }
  #view-ajustes .cuenta-msg--err{ color:var(--adm-danger); }

  /* ---------- Botón "×" de quitar fila y estado "vacío" en los editores
     de Ajustes: los reutilizan varios paneles (comparaciones, galería que
     se expande...), no son exclusivos de uno solo. ---------- */
  .cat-editor-remove{
    flex:0 0 auto;
    width:32px;
    height:32px;
    border-radius:50%;
    border:1px solid rgba(0,0,0,0.14);
    background:transparent;
    color:var(--dim);
    font-size:16px;
    line-height:1;
    cursor:pointer;
    transition:border-color .15s ease, color .15s ease;
  }
  .cat-editor-remove:hover{ border-color:#c0392b; color:#c0392b; }
  .cat-editor-empty{
    font-size:0.85rem;
    color:var(--dim);
    font-style:italic;
  }
  #view-ajustes .cat-editor-remove{
    border-color:var(--adm-border);
    color:var(--adm-dim);
  }
  #view-ajustes .cat-editor-remove:hover{
    border-color:var(--adm-danger);
    color:var(--adm-danger);
  }
  #view-ajustes .cat-editor-empty{ color:var(--adm-dim); }

  /* ---------- Editor "Descubre mi trabajo": foto + nombre + frase ----------
     Cada fila reutiliza el mismo recuadro de subida que el resto de
     Ajustes (ver .ba-slot-box) en miniatura, junto a dos campos de texto
     (nombre y frase corta) y el botón de quitar de .cat-editor-remove. */
  .disc-editor-list{ display:flex; flex-direction:column; gap:12px; margin:16px 0; }
  .disc-editor-item{ display:flex; align-items:center; gap:14px; flex-wrap:wrap; }
  /* Recuadro de vista previa: ancho y no muy alto -misma proporción
     "tira ancha" que tiene la tarjeta real cuando está abierta en la web
     (ver .as-expand-card)-, en vez del recuadro estrecho/vertical de
     antes, que no se parecía en nada a la forma real de la tarjeta. El
     fondo (background-image/-size/-position) se pone inline desde JS con
     la MISMA función que usa la tarjeta real (expandCardBgStyle, ver
     comparison-pairs.js), así lo que se ve aquí es exactamente lo que
     luego se ve en la web: mismo recorte en "Cubrir recuadro", mismo
     margen en "Ver foto entera". */
  .disc-editor-box{
    position:relative;
    flex:0 0 auto;
    width:min(220px, 44vw);
    height:96px;
    border:1.5px dashed rgba(17,17,17,0.32);
    border-radius:10px;
    background-color:rgba(0,0,0,0.02);
    background-repeat:no-repeat;
    display:flex;
    align-items:center;
    justify-content:center;
    cursor:pointer;
    overflow:hidden;
    transition:border-color .2s ease;
  }
  .disc-editor-box:hover{ border-color:var(--accent); }
  .disc-editor-box svg{ width:18px; height:18px; color:var(--dim); flex:none; }
  /* El color de fondo de respaldo (no el borde) es el mismo tono que se
     ve detrás de la foto real en modo "Ver foto entera" -así el margen
     que deja la foto en la vista previa se ve igual que en la web-. */
  .disc-editor-box.has-image{ border-style:solid; border-color:rgba(17,17,17,0.14); background-color:#ffffff; }
  .disc-editor-box.is-uploading{ cursor:default; }
  /* Capa interna de la vista previa (mismo motivo que
     .as-expand-card-photo más arriba: el zoom nunca deforma este
     recuadro, solo se recorta lo que sobra). Aquí es una vista previa
     ESTÁTICA -no se arrastra-: el ajuste de verdad se hace en el
     simulador (ver openExpSimulator en comparison-pairs.js). */
  .disc-editor-box-photo{
    position:absolute;
    inset:0;
    background-size:auto 100%;
    background-position:center;
    background-repeat:no-repeat;
  }
  /* Con foto puesta, el recuadro es una vista previa fiel pero NO se
     arrastra aquí: se toca para abrir el simulador a pantalla completa
     (ver openExpSimulator en comparison-pairs.js), que es donde de
     verdad se ajusta el encuadre con calma y a un tamaño cómodo. */
  .disc-editor-adjust-badge{
    display:inline-flex;
    align-items:center;
    gap:6px;
    margin:auto auto 8px 8px;
    padding:5px 10px 5px 7px;
    border-radius:999px;
    background:rgba(0,0,0,0.55);
    color:#fff;
    font-size:0.72rem;
    font-weight:600;
    pointer-events:none;
  }
  .disc-editor-adjust-badge svg{ width:13px; height:13px; color:#fff; }
  .disc-editor-replace-link{
    align-self:flex-start;
    margin-top:2px;
    padding:0;
    border:none;
    background:none;
    color:var(--accent);
    font-size:0.82rem;
    font-weight:600;
    text-decoration:underline;
    cursor:pointer;
  }

  /* Simulador a pantalla completa (Ajustes → Proyectos): se abre solo al
     subir una foto nueva, o al tocar el recuadro/badge "Ajustar
     encuadre" de una ya puesta. Muestra el recuadro con la MISMA forma
     (ancho ÷ alto) que tiene la tarjeta real cuando está abierta en la
     web -ver getExpandCardAspect en comparison-pairs.js-, a un tamaño
     grande y cómodo para arrastrar la foto con calma antes de confirmar. */
  .exp-sim-overlay{
    position:fixed;
    inset:0;
    z-index:9999;
    background:rgba(10,10,10,0.78);
    display:flex;
    align-items:center;
    justify-content:center;
    padding:20px;
  }
  /* BUG real de la X/"Confirmar" no cerrando nada: display:flex de la
     regla de arriba (con solo el selector de clase) ganaba SIEMPRE al
     estado oculto del atributo hidden, aunque el código sí llegara a
     ejecutar "ocúltate" -el propio atributo hidden nunca llegaba a tener
     efecto visual-. Con el selector [hidden] (más específico que solo la
     clase) el overlay ahora sí desaparece de verdad al cerrarlo. */
  .exp-sim-overlay[hidden]{ display:none; }
  .exp-sim-panel{
    background:#fff;
    border-radius:20px;
    max-width:calc(100vw - 24px);
    /* Antes, si el contenido (recuadro + control de zoom + texto) no
       cabía entero en la pantalla, el panel se salía por arriba y por
       abajo a la vez (por el centrado vertical del overlay) SIN scroll
       propio: la cabecera con la X quedaba empujada fuera de la vista,
       inalcanzable, aunque su botón seguía "funcionando" -solo que no
       se veía ni se podía tocar-. Con tope de alto + scroll propio, el
       panel entero siempre cabe en la pantalla, y si algo no entra se
       puede desplazar dentro del propio panel sin perder la cabecera
       (ver .exp-sim-head, fija arriba del todo). */
    max-height:calc(100vh - 40px);
    overflow-y:auto;
    display:flex;
    flex-direction:column;
    align-items:center;
    box-shadow:0 24px 60px rgba(0,0,0,0.4);
  }
  .exp-sim-head{
    width:100%;
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:12px;
    /* Fija (sticky) arriba del todo del propio scroll del panel: así la
       X para cerrar siempre está a la vista y al alcance, aunque el
       resto del contenido se desplace por debajo. */
    position:sticky;
    top:0;
    padding:18px 18px 8px;
    background:#fff;
    border-radius:20px 20px 0 0;
    z-index:1;
  }
  .exp-sim-body{
    width:100%;
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:12px;
    padding:0 18px 18px;
  }
  .exp-sim-title{
    margin:0;
    font-size:0.98rem;
    font-weight:700;
    color:var(--ink);
  }
  .exp-sim-cancel{
    width:32px;
    height:32px;
    border-radius:50%;
    border:none;
    background:rgba(17,17,17,0.06);
    color:var(--ink);
    font-size:1.1rem;
    line-height:1;
    cursor:pointer;
    flex:none;
  }
  .exp-sim-stage{ display:flex; }
  .exp-sim-box{
    position:relative;
    border-radius:16px;
    overflow:hidden;
    background-color:#ffffff;
    border:1px solid rgba(17,17,17,0.12);
  }
  /* Capa interna que de verdad se agranda/desplaza (transform:scale +
     background-position, ver openExpSimulator en comparison-pairs.js):
     el recuadro de fuera (.exp-sim-box) nunca cambia de tamaño ni de
     forma, solo recorta con overflow:hidden lo que sobra al acercar. */
  .exp-sim-photo{
    position:absolute;
    inset:0;
    background-repeat:no-repeat;
    cursor:grab;
    touch-action:none;
    -webkit-user-select:none;
    user-select:none;
  }
  .exp-sim-photo.is-dragging{ cursor:grabbing; }
  .exp-sim-zoom-row{
    width:100%;
    max-width:420px;
    display:flex;
    align-items:center;
    gap:10px;
  }
  .exp-sim-zoom-icon{
    flex:none;
    width:18px;
    text-align:center;
    color:var(--dim);
    font-size:1.1rem;
    line-height:1;
    user-select:none;
  }
  .exp-sim-zoom{
    flex:1 1 auto;
    accent-color:var(--ink);
  }
  .exp-sim-zoom-value{
    flex:none;
    width:48px;
    text-align:right;
    font-size:0.82rem;
    font-weight:700;
    color:var(--ink);
    font-variant-numeric:tabular-nums;
  }
  .exp-sim-hint{
    margin:0;
    max-width:52ch;
    text-align:center;
    font-size:0.82rem;
    color:var(--dim);
  }
  .exp-sim-actions{ width:100%; display:flex; justify-content:center; }
  .exp-sim-done{
    padding:10px 28px;
    border-radius:999px;
    border:none;
    background:var(--ink);
    color:#fff;
    font-size:0.9rem;
    font-weight:700;
    cursor:pointer;
  }
  .disc-editor-fields{ flex:1 1 220px; min-width:200px; display:flex; flex-direction:column; gap:6px; }
  .disc-editor-fields input{
    padding:8px 11px;
    border-radius:8px;
    border:1px solid rgba(0,0,0,0.14);
    font-family:inherit;
    font-size:0.82rem;
  }
  #view-ajustes .disc-editor-box{ border-color:var(--adm-border); background:var(--adm-surface-2); }
  #view-ajustes .disc-editor-box:hover{ border-color:var(--adm-accent); }
  /* Con foto puesta, el margen debe verse del mismo blanco que en la web
     real (fondo de .as-expand-card), no del tono oscuro del panel de
     Ajustes: si no, la vista previa de "Ver foto entera" mentiría sobre
     cómo queda el margen en la página pública. */
  #view-ajustes .disc-editor-box.has-image{ background-color:#ffffff; }
  #view-ajustes .disc-editor-fields input{
    background:var(--adm-surface-2) !important;
    border:1px solid var(--adm-border) !important;
    color:var(--adm-fg) !important;
    font-family:inherit !important;
  }
  #view-ajustes .disc-editor-fields input:focus{
    outline:none;
    border-color:var(--adm-accent) !important;
    box-shadow:0 0 0 3px var(--adm-accent-dim);
  }
  /* Control de encuadre vertical (evita que la foto se corte por el sitio
     equivocado al expandirse la tarjeta): un slider que mueve el
     background-position-y de la miniatura de la galería expandible. */
  .disc-editor-range-row{
    display:flex;
    align-items:center;
    gap:8px;
    margin-top:2px;
  }
  .disc-editor-range-row label{
    font-size:0.68rem;
    color:var(--dim);
    white-space:nowrap;
  }
  .disc-editor-range-row input[type="range"]{
    flex:1 1 auto;
    min-width:0;
    accent-color:var(--accent);
  }
  #view-ajustes .disc-editor-range-row label{ color:var(--adm-fg); opacity:0.7; }
  /* En modo "Cubrir recuadro" el slider no tiene ningún efecto (la foto
     siempre queda centrada), así que se apaga en vez de dejarlo activo
     sin que haga nada -eso confundía, parecía roto-. Vuelve a activarse
     solo en modo "Ver foto entera". */
  .disc-editor-range-row.is-disabled{ opacity:0.4; }
  .disc-editor-range-row.is-disabled input[type="range"]{ cursor:not-allowed; }

  /* En el comparador de parejas cada columna es más estrecha (110px) que en
     la galería, así que aquí el deslizador va apilado bajo la etiqueta en
     vez de en línea, para no quedarse sin sitio. */
  .pair-editor-item .disc-editor-range-row{
    flex-direction:column;
    align-items:stretch;
    gap:2px;
    margin-top:6px;
  }

  /* ---------- Ajuste de la foto (galería que se expande) ----------
     Antes la foto siempre se recortaba/ampliaba para rellenar el
     recuadro entero ("cover"). Ahora se puede elegir, por foto:
     "Cubrir" (igual que antes, rellena el recuadro) o "Ajustar" (la foto
     entera se ve siempre, sin recortar ni ampliar, dejando un margen de
     color si hace falta). Dos botones pequeños tipo pastilla, con el
     activo resaltado en --accent. */
  .disc-editor-fit-row{
    display:flex;
    align-items:center;
    gap:8px;
    margin-top:2px;
    flex-wrap:wrap;
  }
  .disc-editor-fit-row span{
    font-size:0.68rem;
    color:var(--dim);
    white-space:nowrap;
  }
  #view-ajustes .disc-editor-fit-row span{ color:var(--adm-fg); opacity:0.7; }
  .disc-editor-fit-btn{
    font:inherit;
    font-size:0.7rem;
    font-weight:600;
    padding:4px 10px;
    border-radius:999px;
    border:1px solid rgba(17,17,17,0.16);
    background:transparent;
    color:var(--fg);
    cursor:pointer;
    transition:background .15s ease, color .15s ease, border-color .15s ease;
  }
  #view-ajustes .disc-editor-fit-btn{ border-color:var(--adm-border); color:var(--adm-fg); }
  .disc-editor-fit-btn.active{
    background:var(--accent);
    border-color:var(--accent);
    color:#fff;
  }
  /* Explica en una línea qué hace el modo elegido, justo debajo de los
     dos botones -así no hace falta adivinar qué significa "cubrir" o
     "ver entera" con solo el nombre del botón-. */
  .disc-editor-fit-hint{
    margin:4px 0 0;
    font-size:0.68rem;
    line-height:1.35;
    color:var(--dim);
  }
  #view-ajustes .disc-editor-fit-hint{ color:var(--adm-fg); opacity:0.6; }

  /* ---------- Editor de parejas del comparador con flechas ----------
     Cada fila son dos recuadros de subida (sin editar / editada, mismo
     patrón que .ba-slot-box) más el botón de quitar la pareja entera. */
  .pair-editor-list{ display:flex; flex-direction:column; gap:16px; margin:16px 0; }
  .pair-editor-item{ display:flex; align-items:center; gap:10px; }
  .pair-editor-item .ba-slots{ flex:1; margin:0; align-items:flex-start; }
  .pair-editor-item .ba-slot{ max-width:110px; flex-direction:column; align-items:stretch; }
  /* En esta lista compacta el máximo de alto es menor que en Ajustes,
     para que una foto muy vertical no dispare la altura de toda la fila. */
  .pair-editor-item .ba-slot-box.has-image{ max-height:146px; }
  .pair-editor-item .ba-slot-box.has-image img{ max-height:146px; }

  /* Columna de "mover arriba / mover abajo / quitar", al final de la fila.
     La reutilizan tanto el editor de parejas como el de la galería que se
     expande (ver .disc-editor-item más abajo), para poder reordenar sin
     tener que borrar y volver a añadir desde cero. */
  .pair-editor-actions{
    display:flex;
    flex-direction:column;
    align-items:center;
    gap:6px;
    flex:0 0 auto;
  }
  .pair-editor-move{
    width:30px;
    height:30px;
    border-radius:50%;
    border:1px solid rgba(0,0,0,0.14);
    background:transparent;
    color:var(--dim);
    font-size:14px;
    line-height:1;
    cursor:pointer;
    transition:border-color .15s ease, color .15s ease, opacity .15s ease;
  }
  .pair-editor-move:hover:not(:disabled){ border-color:var(--accent); color:var(--accent); }
  .pair-editor-move:disabled{ opacity:0.3; cursor:default; }
  #view-ajustes .pair-editor-move{
    border-color:var(--adm-border);
    color:var(--adm-dim);
  }
  #view-ajustes .pair-editor-move:hover:not(:disabled){
    border-color:var(--adm-accent);
    color:var(--adm-accent);
  }

  #view-ajustes .historial-list{
    background:var(--adm-surface-2);
    border:1px solid var(--adm-border);
    border-radius:10px;
  }
  #view-ajustes .historial-item{ border-bottom:1px solid var(--adm-border); }
  #view-ajustes .historial-item-action{ color:var(--adm-fg); }
  #view-ajustes .historial-item-time{ color:var(--adm-dim); }
  #view-ajustes .historial-item-detail{ color:var(--adm-dim); }
  #view-ajustes .historial-empty{ color:var(--adm-dim); }

  /* ---------- Editor de categorías de "Proyectos" (Deportes, Fiestas...)
     ----------
     Cada categoría es una "tarjeta" con su nombre editable arriba y, debajo,
     su propia mini-cuadrícula de fotos (miniatura + botón de quitar, más
     un hueco de "añadir" al final) -así, crear una categoría nueva crea a
     la vez su propio apartado para subir imágenes, tal cual se pidió-. */
  .cat-editor-list{ display:flex; flex-direction:column; gap:14px; margin:16px 0; }
  .cat-editor-item{
    display:flex;
    flex-direction:column;
    gap:10px;
    padding:14px;
    border:1px solid rgba(0,0,0,0.1);
    border-radius:12px;
    background:rgba(0,0,0,0.02);
  }
  .cat-editor-item-head{ display:flex; align-items:center; gap:10px; }
  .cat-editor-name{
    flex:1 1 auto;
    min-width:0;
    padding:8px 11px;
    border-radius:8px;
    border:1px solid rgba(0,0,0,0.14);
    font-family:inherit;
    font-size:0.88rem;
    font-weight:600;
  }
  .cat-editor-photos{ display:flex; flex-wrap:wrap; gap:8px; }
  .cat-editor-link{
    padding:8px 11px;
    border-radius:8px;
    border:1px solid rgba(0,0,0,0.14);
    font-family:inherit;
    font-size:0.82rem;
  }
  .cat-editor-thumb{
    position:relative;
    width:60px;
    height:60px;
    flex:none;
    border-radius:8px;
    overflow:hidden;
    background:#000;
    border:1px solid rgba(17,17,17,0.14);
  }
  .cat-editor-thumb img{ width:100%; height:100%; object-fit:cover; display:block; }
  .cat-editor-thumb button{
    position:absolute;
    top:2px;
    right:2px;
    width:18px;
    height:18px;
    border-radius:50%;
    border:none;
    background:rgba(0,0,0,0.6);
    color:#fff;
    display:flex;
    align-items:center;
    justify-content:center;
    cursor:pointer;
    padding:0;
  }
  .cat-editor-thumb button svg{ width:10px; height:10px; }
  .cat-editor-thumb button:hover{ background:var(--accent); }
  .cat-editor-add-photo{
    flex:none;
    width:60px;
    height:60px;
    border-radius:8px;
    border:1.5px dashed rgba(17,17,17,0.32);
    background:rgba(0,0,0,0.02);
    display:flex;
    align-items:center;
    justify-content:center;
    cursor:pointer;
    color:var(--dim);
    transition:border-color .2s ease;
  }
  .cat-editor-add-photo:hover{ border-color:var(--accent); }
  .cat-editor-add-photo svg{ width:18px; height:18px; }
  .cat-editor-add-photo.is-uploading{ font-size:0.6rem; color:var(--dim); cursor:default; text-align:center; padding:4px; }
  #view-ajustes .cat-editor-item{ border-color:var(--adm-border); background:var(--adm-surface-2); }
  #view-ajustes .cat-editor-name{ border-color:var(--adm-border); background:var(--adm-surface); color:var(--adm-fg); }
  #view-ajustes .cat-editor-link{ border-color:var(--adm-border); background:var(--adm-surface); color:var(--adm-fg); }
  #view-ajustes .cat-editor-thumb{ border-color:var(--adm-border); }
  #view-ajustes .cat-editor-add-photo{ border-color:var(--adm-border); background:var(--adm-surface); color:var(--adm-dim); }
  #view-ajustes .cat-editor-add-photo:hover{ border-color:var(--adm-accent); }

  /* ---------- Etiquetas "Editado"/"Creado" del editor de YouTube ----------
     Dos botones independientes por vídeo (ver .yt-tag-toggle en
     youtube-videos.js): cada uno se puede activar o no sin afectar al
     otro, así un vídeo puede llevar ninguna etiqueta, una o las dos. El
     estado activo se refleja también en el sticker de la tarjeta pública
     (ver .youtube-card-sticker más abajo), con los mismos colores. */
  .yt-tag-row{ display:flex; gap:8px; }
  .yt-tag-toggle{
    padding:6px 14px;
    border-radius:999px;
    border:1px solid rgba(0,0,0,0.16);
    background:transparent;
    color:var(--dim);
    font-family:inherit;
    font-size:0.78rem;
    font-weight:600;
    cursor:pointer;
    transition:border-color .15s ease, color .15s ease, background-color .15s ease;
  }
  .yt-tag-toggle:hover{ border-color:var(--accent); color:var(--accent); }
  .yt-tag-toggle--edited.is-active{
    border-color:#2f7d4f;
    background:#2f7d4f;
    color:#fff;
  }
  .yt-tag-toggle--created.is-active{
    border-color:#2f5fa8;
    background:#2f5fa8;
    color:#fff;
  }
  #view-ajustes .yt-tag-toggle{ border-color:var(--adm-border); color:var(--adm-dim); }
  #view-ajustes .yt-tag-toggle:hover{ border-color:var(--adm-accent); color:var(--adm-accent); }
  #view-ajustes .yt-tag-toggle--edited.is-active,
  #view-ajustes .yt-tag-toggle--created.is-active{ color:#fff; }

  /* ---------- Sticker "Editado"/"Creado" sobre la miniatura pública ----------
     Se pintan solo en la cuadrícula pública de vídeos (.youtube-card-thumb),
     no en el editor de Ajustes. Van en la esquina superior izquierda, uno
     debajo del otro si el vídeo lleva las dos etiquetas a la vez. */
  .youtube-card-stickers{
    position:absolute;
    top:8px;
    left:8px;
    display:flex;
    flex-direction:column;
    gap:4px;
    z-index:1;
  }
  .youtube-card-sticker{
    padding:3px 9px;
    border-radius:999px;
    font-family:inherit;
    font-size:0.68rem;
    font-weight:700;
    letter-spacing:0.02em;
    color:#fff;
    text-transform:uppercase;
    box-shadow:0 2px 6px rgba(0,0,0,0.35);
  }
  .youtube-card-sticker--edited{ background:#2f7d4f; }
  .youtube-card-sticker--created{ background:#2f5fa8; }
