/* =========================================================
   WHITE SALON — Hoja de estilos propia
   Paleta: estrictamente blanco / negro / grises. Sin color de acento
   (nada de naranja/ámbar). Bootstrap 5 se usa como base de grid y
   componentes; este archivo sobreescribe variables y añade el look
   editorial en blanco y negro de la marca.
   ========================================================= */

:root {
  --ws-black: #0a0a0a;
  --ws-black-soft: #161616;
  --ws-white: #ffffff;
  --ws-gray-100: #f5f5f5;
  --ws-gray-200: #e7e7e7;
  --ws-gray-400: #b3b3b3;
  --ws-gray-600: #6e6e6e;
  --ws-gray-800: #2b2b2b;

  /* Sobreescribe variables de Bootstrap para que TODO el sistema
     (botones, links, focus-ring, etc.) herede la paleta B/N */
  --bs-primary: var(--ws-black);
  --bs-primary-rgb: 10, 10, 10;
  --bs-link-color: var(--ws-black);
  --bs-link-hover-color: var(--ws-gray-600);
  /* 03-sep-2026: el color real que Bootstrap 5.3 usa en el <a> del
     reboot es --bs-link-color-RGB (no --bs-link-color a secas) — sin
     esta línea cualquier link "plano" (sin clase propia, como los que
     había en "Otras formas de contacto" de contacto.php) se veía azul
     por defecto en vez de negro, aunque las dos líneas de arriba ya
     estuvieran puestas. */
  --bs-link-color-rgb: 10, 10, 10;
  --bs-link-hover-color-rgb: 110, 110, 110;
  --bs-body-color: var(--ws-black);
  --bs-body-font-family: "Jost", "Helvetica Neue", Arial, sans-serif;
}

body { font-family: var(--bs-body-font-family); }

body {
  color: var(--ws-black);
  background-color: var(--ws-white);
}

/* Regla directa (03-sep-2026): las variables --bs-link-color-rgb de
   arriba no bastaron para poner los links en negro — Bootstrap 5.3
   sigue mostrándolos azules en algunos casos. En vez de perseguir el
   nombre exacto de la variable interna, esta regla fija el color a
   propósito: cualquier <a> "plano" (sin su propia clase de color, como
   .nav-link, .footer-ws a, .btn, etc., que siguen mandando por ser más
   específicas) queda en negro por defecto y gris al pasar el mouse, sin
   depender de cómo Bootstrap arme sus variables internas. */
a {
  color: var(--ws-black);
  text-decoration-color: var(--ws-gray-400);
}
a:hover {
  color: var(--ws-gray-600);
}

/* ---------- Tipografía ---------- */
/* Todos los títulos (h1-h6, incluidos los que ya llevan su propia
   clase .h5/.h6 de tamaño) van en mayúsculas para resaltarlos más —
   look editorial, igual que .eyebrow. Donde un título ya trae su propio
   letter-spacing puntual (footer, tarjetas de sede) esa regla más
   específica sigue ganando, así que no hay que tocar nada más. */
h1, h2, h3, h4, h5, h6, .display-1, .display-2, .display-3 {
  font-weight: 700;
  letter-spacing: 0.01em;
  text-transform: uppercase;
}

.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.18em;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--ws-gray-600);
}

.section-white .eyebrow { color: var(--ws-gray-600); }
.section-black .eyebrow { color: var(--ws-gray-400); }

/* ---------- Texto justificado ----------
   Los párrafos de contenido largo (descripciones de servicios, "Sobre
   White Salon", artículos del blog, footer) van justificados: líneas
   parejas a ambos lados, look más editorial. hyphens:auto deja partir
   palabras largas al final de línea para que el navegador no tenga que
   estirar tanto el espacio entre palabras para justificar — así se
   evitan los huecos grandes. line-height más alto le da más aire a
   cada línea. Los subtítulos cortos y centrados (intro del hero,
   encabezados de página, .eyebrow) se dejan tal cual — un texto corto
   y centrado no gana nada al justificarse. */
p.text-secondary,
.footer-ws p {
  text-align: justify;
  text-justify: inter-word;
  hyphens: auto;
  line-height: 1.7;
}

/* Los párrafos cortos en columnas angostas (texto de tarjetas de
   servicio/sede/blog) son los que más se notaban con huecos grandes al
   justificar: con pocas palabras por línea el navegador tiene que
   estirar mucho el espacio para llenar el ancho. Ahí se ven mejor
   alineados a la izquierda, sin justificar. */
p.text-secondary.small {
  text-align: left;
  hyphens: none;
}

/* ---------- Más espacio entre elementos ----------
   Un poco más de aire entre bloques que estaban muy pegados: las filas
   de tarjetas (servicios, sedes, blog, estadísticas) cuando se apilan
   en pantallas angostas, y los ítems de las listas de check dentro de
   servicios.php. */
.row.g-4 { --bs-gutter-y: 2.5rem; }
.list-unstyled.text-secondary.gap-2 { gap: 1rem !important; }

/* ---------- Logo oficial ----------
   assets/img/logo.png: wordmark completo (WHITE + anillo "WS" + SALON),
   negro sobre fondo transparente. assets/img/logo-mark.png: solo el
   anillo "WS", para usos compactos si algún día se necesita. */
.ws-logo {
  display: inline-flex;
  align-items: center;
  color: inherit;
  text-decoration: none;
}
.ws-logo-img {
  height: 34px;
  width: auto;
  display: block;
}
/* El logo es negro sobre transparente; en fondos negros (footer) se
   invierte para que quede blanco, sin necesitar un segundo archivo. */
.ws-logo-img--invert { filter: invert(1); }

/* ---------- Navbar ---------- */
.navbar-ws {
  background-color: var(--ws-white);
  border-bottom: 1px solid var(--ws-gray-200);
  transition: background-color .25s ease, box-shadow .25s ease;
}
.navbar-ws.is-scrolled {
  box-shadow: 0 2px 14px rgba(0,0,0,.06);
}
.navbar-ws .nav-link {
  position: relative;
  color: var(--ws-black);
  font-size: 0.92rem;
  letter-spacing: 0.03em;
  margin-inline: .35rem;
}
.navbar-ws .nav-link.active,
.navbar-ws .nav-link:hover {
  color: var(--ws-gray-600);
}
.navbar-ws .nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 1px;
  background-color: var(--ws-black);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .3s ease;
}
.navbar-ws .nav-link.active::after,
.navbar-ws .nav-link:hover::after {
  transform: scaleX(1);
  transform-origin: left;
}
/* Centrado real del menú (03-sep-2026): al quitar el botón "Reservar"
   del navbar solo quedaron 2 bloques (logo + menú), y con
   justify-content:space-between (default de Bootstrap en .navbar >
   .container) el menú quedaba pegado a la derecha en vez de centrado.
   De lg para arriba (mismo punto donde el menú deja de ser el dropdown
   colapsable) lo centramos de verdad con position:absolute + 50%, así
   queda a la misma distancia del logo que del borde derecho sin
   importar el ancho de ninguno de los dos.
   04-sep-2026: se probó (y se descartó) un menú partido en dos mitades
   a los lados del logo — Santiago pidió volver a este menú centrado de
   siempre. */
@media (min-width: 992px) {
  .navbar-ws .container { position: relative; }
  .navbar-ws .navbar-nav {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  }
}

/* ---------- Botones ---------- */
.btn-ws-dark {
  background-color: var(--ws-black);
  border: 1px solid var(--ws-black);
  color: var(--ws-white);
  letter-spacing: .04em;
  padding: .75rem 1.75rem;
  border-radius: 0;
  transition: background-color .2s ease, color .2s ease, transform .25s ease, box-shadow .25s ease;
}
.btn-ws-dark:hover { background-color: var(--ws-gray-800); color: var(--ws-white); transform: translateY(-2px); box-shadow: 0 12px 26px -12px rgba(0,0,0,.5); }

.btn-ws-outline-light {
  background-color: transparent;
  border: 1px solid var(--ws-white);
  color: var(--ws-white);
  letter-spacing: .04em;
  padding: .75rem 1.75rem;
  border-radius: 0;
  transition: background-color .2s ease, color .2s ease, transform .25s ease;
}
.btn-ws-outline-light:hover { background-color: var(--ws-white); color: var(--ws-black); transform: translateY(-2px); }

.btn-ws-outline-dark {
  background-color: transparent;
  border: 1px solid var(--ws-black);
  color: var(--ws-black);
  letter-spacing: .04em;
  padding: .75rem 1.75rem;
  border-radius: 0;
  transition: background-color .2s ease, color .2s ease, transform .25s ease;
}
.btn-ws-outline-dark:hover { background-color: var(--ws-black); color: var(--ws-white); transform: translateY(-2px); }

/* Botón flotante de WhatsApp (icono vía Bootstrap Icons).
   04-sep-2026: Santiago pidió que este ícono (el único que flota solo,
   sin texto, en todas las páginas) use el verde original de WhatsApp
   en vez del negro del resto del sitio — es la única excepción a la
   paleta blanco/negro/gris, a propósito, para que se reconozca de
   inmediato como el botón de WhatsApp. Los demás íconos de WhatsApp del
   sitio (dentro de botones con texto, como "Reservar por WhatsApp") se
   quedan en negro/blanco, sin cambio. */
.btn-whatsapp-float {
  position: fixed;
  right: 1.25rem;
  /* 05-sep-2026: mientras el aviso de cookies esté visible, script.js le
     pone a este botón un "bottom" en línea (px) igual a este valor base
     más el alto real del aviso, para que suba y no quede tapado — y lo
     regresa a este valor exacto (1.25rem = 20px, con la tipografía base
     de 16px del sitio) al aceptar. Ver .cookie-banner más abajo. */
  bottom: 1.25rem;
  z-index: 1040;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background-color: #25d366;
  color: var(--ws-white);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
  box-shadow: 0 6px 20px rgba(0,0,0,.25);
  transition: transform .2s ease, bottom .2s ease;
}
.btn-whatsapp-float:hover { transform: scale(1.08); color: var(--ws-white); }

/* ---------- Aviso de cookies ----------
   05-sep-2026: barra fija abajo de todo, negra (como el resto de
   elementos "de sistema" del sitio, ej. el navbar), con el texto a la
   izquierda y el botón "Aceptar" a la derecha. En pantallas angostas
   pasa a columna (texto arriba, botón abajo, todo centrado). Se oculta
   con el atributo HTML "hidden" (ver includes/footer.php y script.js),
   nunca con display:none puesto a mano, para que sea consistente con
   cómo el navegador ya maneja ese atributo. */
.cookie-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1045;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 1rem;
  background-color: var(--ws-black);
  color: var(--ws-gray-400);
  padding: 1rem 1.5rem;
  box-shadow: 0 -6px 20px rgba(0,0,0,.25);
}
.cookie-banner[hidden] {
  display: none;
}
.cookie-banner__texto {
  max-width: 640px;
  font-size: .85rem;
  line-height: 1.5;
}
.cookie-banner__texto a {
  color: var(--ws-white);
}
.cookie-banner__texto a:hover {
  color: var(--ws-gray-400);
}
.cookie-banner__btn {
  flex-shrink: 0;
}
@media (max-width: 575.98px) {
  .cookie-banner {
    flex-direction: column;
    text-align: center;
    padding: 1.25rem 1rem;
  }
}

/* ---------- Secciones blanco / negro intercaladas ---------- */
.section-black {
  background-color: var(--ws-black);
  color: var(--ws-white);
  background-image: radial-gradient(rgba(255,255,255,.05) 1px, transparent 1px);
  background-size: 22px 22px;
}
.section-black .text-muted-ws { color: var(--ws-gray-400) !important; }
.section-white { background-color: var(--ws-white); color: var(--ws-black); }
.section-gray  { background-color: var(--ws-gray-100); color: var(--ws-black); }

/* ---------- CTA final (home): "Agenda aquí" / "¿Lista para tu
   próxima cita?" más grandes que un eyebrow/h2 normal, para que la
   última sección del home cierre con fuerza. Escalado solo aquí (no
   se toca .eyebrow ni h2 en general, que se usan en todo el sitio). */
.cta-final .eyebrow {
  font-size: 1.05rem;
  letter-spacing: 0.2em;
}
.cta-final h2 {
  font-size: clamp(2.5rem, 2rem + 2vw, 4rem);
}

/* ---------- Hero ---------- */
.hero-ws {
  position: relative;
  background-color: var(--ws-black);
  color: var(--ws-white);
  min-height: 82vh;
  display: flex;
  align-items: center;
  overflow: hidden;
}
.hero-ws .hero-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  opacity: .45;
}
.hero-ws .hero-overlay {
  position: absolute;
  inset: 0;
  z-index: 3;
  background: linear-gradient(180deg, rgba(10,10,10,.35) 0%, rgba(10,10,10,.85) 100%);
}
/* Halos suaves en escala de grises que le dan movimiento al hero mientras
   no hay video/foto real de fondo. En cuanto el video arranca (ver
   .has-video más abajo) se apagan solos para no competir con la imagen. */
.hero-ws .hero-aurora {
  position: absolute;
  inset: -20%;
  z-index: 0;
  background:
    radial-gradient(closest-side, rgba(255,255,255,.10), transparent 70%) 15% 25% / 45% 55% no-repeat,
    radial-gradient(closest-side, rgba(255,255,255,.07), transparent 70%) 85% 75% / 40% 50% no-repeat;
  animation: ws-aurora 18s ease-in-out infinite alternate;
  transition: opacity .8s ease;
  pointer-events: none;
}
/* Video de fondo del hero (opcional). Si assets/video/hero.mp4 no existe
   todavía, o el navegador no lo puede reproducir, este elemento se queda
   invisible (opacity:0) y el hero se ve igual que siempre, con los halos
   arriba. Cuando el video carga y arranca, script.js agrega la clase
   .has-video a .hero-ws y aparece con un fundido.
   Se fuerza a escala de grises por CSS: así, sin importar el video real
   que se suba (seguro va a tener color), el hero se mantiene estrictamente
   blanco y negro, como pide la marca. */
.hero-ws .hero-video {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.05) brightness(.85);
  opacity: 0;
  transition: opacity 1.2s ease;
  pointer-events: none;
}
.hero-ws.has-video .hero-video { opacity: .55; }
.hero-ws.has-video .hero-aurora { opacity: 0; }

/* Video de YouTube de fondo (ver el comentario en index.php). El iframe
   lo crea script.js — este wrapper arranca invisible y solo se muestra
   si JS agrega la clase .has-yt-video (así, si el visitante prefiere
   menos movimiento, el bloque nunca aparece y se queda el halo). El
   truco de vw/vh es el estándar para que un iframe de YouTube cubra
   todo el contenedor sin bordes negros, sin importar el aspecto de
   pantalla (equivalente a object-fit:cover en un <video> normal). */
.hero-yt-wrap {
  position: absolute;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.2s ease;
}
.hero-ws.has-yt-video .hero-yt-wrap { opacity: 1; }
.hero-ws.has-yt-video .hero-aurora { opacity: 0; }
.hero-yt-wrap iframe {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100vw;
  height: 56.25vw; /* 16:9 */
  min-height: 100%;
  min-width: 177.78vh; /* 16:9 */
  transform: translate(-50%, -50%);
  filter: grayscale(1) contrast(1.05) brightness(.85);
  border: 0;
}
.hero-ws .hero-grain {
  position: absolute;
  inset: 0;
  z-index: 2;
  opacity: .06;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}
.hero-ws .hero-content { position: relative; z-index: 4; }

/* Entrada escalonada del texto del hero al cargar la página */
.hero-content .eyebrow,
.hero-content h1,
.hero-content .lead,
.hero-content .d-flex {
  opacity: 0;
  animation: ws-fade-up .8s ease forwards;
}
.hero-content .eyebrow { animation-delay: .1s; }
.hero-content h1       { animation-delay: .25s; }
.hero-content .lead    { animation-delay: .45s; }
.hero-content .d-flex  { animation-delay: .65s; }

/* ---------- Tarjetas de servicio: video/foto de fondo ----------
   Cada línea de servicio se ve como una foto o video real de fondo
   (recorte 4:5), en blanco y negro, con un degradado oscuro abajo y el
   título + descripción + enlace encima en blanco — como una portada
   editorial, no una tarjeta plana con ícono.
   Si el video de esa tarjeta (assets/video/<servicio>.mp4) todavía no
   existe, se ve el placeholder a rayas (mismo que el resto del sitio,
   ver .img-placeholder) en vez de romperse: misma degradación elegante
   que el resto de los videos del sitio — en cuanto Santiago suba el
   archivo, aparece solo, sin tocar código. */
.card-media-servicio {
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  color: var(--ws-white);
  isolation: isolate;
}
.card-media-servicio__video,
.card-media-servicio__placeholder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
  transition: transform .6s ease;
}
.card-media-servicio__video {
  filter: grayscale(1) contrast(1.05) brightness(.85);
  opacity: 0;
  transition: opacity .8s ease, transform .6s ease;
  pointer-events: none;
}
.card-media-servicio.has-video .card-media-servicio__video { opacity: 1; }
.card-media-servicio.has-video .card-media-servicio__placeholder { opacity: 0; }
.card-media-servicio:hover .card-media-servicio__video,
.card-media-servicio:hover .card-media-servicio__placeholder { transform: scale(1.06); }
.card-media-servicio__overlay {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(10,10,10,0) 38%, rgba(10,10,10,.9) 100%);
  pointer-events: none;
}
.card-media-servicio__content {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  padding: 1.85rem;
}
.card-media-servicio__content h3 {
  font-size: 1.3rem;
  margin: 0 0 .6rem;
}
.card-media-servicio__content p {
  font-size: .88rem;
  line-height: 1.55;
  color: var(--ws-gray-200);
  margin: 0 0 1.1rem;
  max-width: 22rem;
}
.card-media-servicio__link {
  display: inline-block;
  color: var(--ws-white);
  font-size: .84rem;
  letter-spacing: .04em;
  text-decoration: none;
  border-bottom: 1px solid rgba(255,255,255,.4);
  padding-bottom: 2px;
  transition: border-color .2s ease;
}
.card-media-servicio__link:hover { border-color: var(--ws-white); }
/* .img-placeholder trae position:relative por defecto (para las tarjetas
   normales) — acá necesita quedar absoluto y a pantalla completa como su
   hermano el video, así que se pisa con un selector más específico en vez
   de depender del orden en el archivo. */
.card-media-servicio__placeholder.img-placeholder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* El rayado normal es claro (pensado para texto oscuro encima); acá el
     título/descripción van en blanco sobre esta misma zona, así que se
     oscurece para que el texto blanco se siga leyendo incluso arriba de
     la tarjeta, donde el degradado de abajo todavía no oscurece nada. */
  background: repeating-linear-gradient(135deg, var(--ws-gray-800), var(--ws-gray-800) 10px, #1a1a1a 10px, #1a1a1a 20px);
}
/* El texto propio del placeholder ("Foto/video real — pendiente") está
   pensado para cajas vacías sin nada más encima. Acá el título y la
   descripción reales van superpuestos casi en toda la tarjeta (mientras
   no llegue el video de Peluquería/Uñas), así que no hay dónde ponerlo
   sin que se cruce con ese texto: se oculta solo visualmente (sigue en
   el código, por si se necesita) y basta con el patrón rayado para
   indicar "falta contenido real", igual que en el resto del sitio. */
.card-media-servicio__placeholder.img-placeholder {
  font-size: 0;
}

/* ---------- Tarjetas de sede ---------- */
.card-sede {
  border: 1px solid var(--ws-gray-200);
  padding: 1.75rem;
  height: 100%;
  transition: transform .3s ease, box-shadow .3s ease, border-color .3s ease;
}
/* 04-sep-2026: en sedes.php la dirección y el horario no ocupan el mismo
   número de líneas en cada sede (una dirección de una línea vs. dos, un
   horario de dos rangos vs. tres) — como las tarjetas de una misma fila
   se estiran a la misma altura (Bootstrap), el mapa y el botón quedaban
   cada uno a una altura distinta dentro de la tarjeta, con un margen
   inferior distinto en cada una. Ahora .card-sede es una columna flex y
   el mapa + botón van en un bloque con "mt-auto" (ver sedes.php), así
   ese bloque siempre queda pegado abajo, con el mismo tamaño y el mismo
   margen en las 6 tarjetas sin importar cuánto texto tenga cada una
   arriba. */
.card-sede {
  display: flex;
  flex-direction: column;
}
.card-sede:hover {
  transform: translateY(-5px);
  box-shadow: 0 20px 38px -20px rgba(0,0,0,.25);
  border-color: var(--ws-black);
}
.card-sede h3 { font-size: 1.1rem; text-transform: uppercase; letter-spacing: .04em; }

/* ---------- Tarjetas-resumen "Barranquilla" / "Soledad" del home ----------
   04-sep-2026: antes eran ".card-sede" con un badge + un <h3>"3 sedes"</h3>
   + un párrafo con las 3 sedes separadas por "·" en una sola línea — se
   veían planas. Clase propia (no ".card-sede", que también usan las
   tarjetas con mapa de sedes.php) con: un encabezado con línea divisoria
   (ciudad a la izquierda, cantidad a la derecha) y cada sede en su
   propia línea con un ícono de pin, para que se lea más como una lista
   de lugares reales y no como una sola frase larga. */
.card-ciudad {
  border: 1px solid var(--ws-gray-200);
  padding: 2rem;
  height: 100%;
  transition: transform .3s ease, box-shadow .3s ease, border-color .3s ease;
}
.card-ciudad:hover {
  transform: translateY(-5px);
  box-shadow: 0 20px 38px -20px rgba(0,0,0,.25);
  border-color: var(--ws-black);
}
.card-ciudad__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 1.25rem;
  margin-bottom: 1.25rem;
  border-bottom: 1px solid var(--ws-gray-200);
}
.card-ciudad__count {
  font-size: 1rem;
  color: var(--ws-gray-600);
}
.card-ciudad__lista {
  list-style: none;
  padding: 0;
  margin: 0;
}
.card-ciudad__lista li {
  display: flex;
  align-items: center;
  gap: .65rem;
  padding: .55rem 0;
  font-size: 1.05rem;
}
.card-ciudad__lista li + li {
  border-top: 1px solid var(--ws-gray-100);
}
.card-ciudad__lista i {
  color: var(--ws-gray-400);
  font-size: .9rem;
}

/* 04-sep-2026: el horario de cada sede tiene varios rangos de días (ej.
   "Lun a mié: ..." + "Jue a sáb: ..." + "Dom: ..."). Antes iban todos
   en un solo texto separado por "·" dentro de un <p>, y al hacer wrap
   en pantallas angostas la segunda línea quedaba pegada al margen, sin
   alinear con el texto de la primera línea y sin relación clara entre
   los rangos. Ahora cada rango es su propio renglón (<li>), alineados
   en columna junto al ícono del reloj (que queda fijo arriba, sin
   repetirse en cada línea). */
.card-sede__horario {
  display: flex;
  align-items: flex-start;
  gap: .55rem;
}
.card-sede__horario i {
  margin-top: .2rem;
  flex-shrink: 0;
}
.card-sede__horario ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.card-sede__horario li {
  line-height: 1.5;
}

/* ---------- Panel de "Otras formas de contacto" (contacto.php) ----------
   03-sep-2026: mismo look que .card-sede (borde, padding, hover con
   levantada) pero SIN su "height:100%" — .card-sede está pensado para
   tarjetas en una grilla de altura pareja; aquí son 3 cajas apiladas
   junto al formulario, así que el height:100% las estiraba a la altura
   del formulario completo (mucho más alto) y dejaba un vacío enorme
   adentro de cada una. */
.contact-panel {
  border: 1px solid var(--ws-gray-200);
  padding: 1.75rem;
  transition: transform .3s ease, box-shadow .3s ease, border-color .3s ease;
}
.contact-panel:hover {
  transform: translateY(-5px);
  box-shadow: 0 20px 38px -20px rgba(0,0,0,.25);
  border-color: var(--ws-black);
}
.contact-panel h3 { font-size: 1.1rem; text-transform: uppercase; letter-spacing: .04em; }

/* Links de WhatsApp/Instagram dentro del panel de arriba — antes eran
   <a> sin clase, se veían azules y subrayados por defecto de Bootstrap
   (ver el arreglo de --bs-link-color-rgb más arriba). Ahora quedan en
   negro, sin subrayado, con el ícono en gris y un pequeño desplazamiento
   al pasar el mouse — mismo lenguaje "editorial minimal" del resto del
   sitio. */
.contact-link {
  display: flex;
  align-items: center;
  gap: .6rem;
  color: var(--ws-black);
  text-decoration: none;
  font-size: .95rem;
  padding: .4rem 0;
  transition: color .2s ease, padding-left .2s ease;
}
.contact-link + .contact-link { border-top: 1px solid var(--ws-gray-200); }
.contact-link i { font-size: 1.05rem; color: var(--ws-gray-600); transition: color .2s ease; }
.contact-link:hover { color: var(--ws-gray-600); padding-left: .3rem; }
.contact-link:hover i { color: var(--ws-black); }

/* Link con flecha y subrayado fino (ej. "Ver direcciones y horarios
   completos" en contacto.php) — mismo tratamiento que el resto de los
   enlaces con flecha del sitio. */
.link-underline {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  color: var(--ws-black);
  font-size: .85rem;
  letter-spacing: .03em;
  text-decoration: none;
  border-bottom: 1px solid var(--ws-gray-400);
  padding-bottom: 3px;
  transition: border-color .2s ease, gap .2s ease;
}
.link-underline:hover { border-color: var(--ws-black); gap: .7rem; }
/* Mini-mapa de cada tarjeta de sede. Esquinas rectas y borde delgado,
   mismo lenguaje que el resto de las tarjetas.
   04-sep-2026: Santiago pidió que todos los mapas del sitio se vean a
   color (antes iban en blanco y negro, igual que fotos y videos) — se
   quitó el filtro grayscale de este mini-mapa y del mapa panorámico de
   abajo; el resto de la paleta (marcadores, controles, popups) sigue en
   negro/blanco.
   04-sep-2026 (más tarde el mismo día): dejó de ser un iframe de Google
   (que a veces mostraba el pin de OTRAS sedes además de la propia, ver
   sedes.php) y pasó a ser un mapa propio con Leaflet, igual que el
   panorámico del home, con un solo marcador — por eso ahora el
   contenedor es un <div> (necesita su propia altura fija, Leaflet no la
   calcula solo) en vez de un <iframe>. */
.card-sede__mapa {
  overflow: hidden;
  border: 1px solid var(--ws-gray-200);
}
.card-sede__mapa iframe,
.card-sede__mapa .mapa-sede-mini {
  display: block;
  width: 100%;
  height: 190px;
  border: 0;
  background-color: var(--ws-gray-100);
}
/* Mismo problema de especificidad que ".mapa-sedes-ws.leaflet-container"
   más abajo: Leaflet pone "leaflet-container" en el mismo <div> que ya
   tiene "mapa-sede-mini", no en un hijo. */
.mapa-sede-mini.leaflet-container {
  font-family: inherit;
}
.mapa-sede-mini .leaflet-control-attribution {
  font-size: 9px;
  background: rgba(255, 255, 255, .75);
  padding: 0 4px;
}
/* Mapa panorámico con las 6 sedes, debajo del resumen "Dónde
   encontrarnos" del home. Desde 03-sep-2026 es un mapa propio con
   Leaflet (ver index.php), así que el contenedor necesita su propia
   altura fija (Leaflet no la calcula solo); antes solo el <iframe> de
   Google la tenía. */
.mapa-sedes-ws {
  overflow: hidden;
  border: 1px solid var(--ws-gray-200);
  height: 420px;
  background-color: var(--ws-gray-100);
}
.mapa-sedes-ws iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}
/* 04-sep-2026: el selector de abajo era ".mapa-sedes-ws .leaflet-container"
   (con espacio = descendiente), pero Leaflet pone la clase
   "leaflet-container" en el MISMO div que ya tiene "mapa-sedes-ws" — no
   en un hijo — así que nunca coincidía. Corregido a selector compuesto
   (sin espacio). El mosaico (.leaflet-tile-pane) ya no lleva filtro de
   blanco y negro — se dejó a color a pedido de Santiago. */
.mapa-sedes-ws.leaflet-container {
  font-family: inherit;
  background-color: var(--ws-gray-100);
}
/* Controles de zoom (+/-) y popups: esquinas rectas y blanco/negro, igual
   que el resto del sitio (nada de los azules/redondeados por defecto de
   Leaflet). */
.mapa-sedes-ws .leaflet-control-zoom {
  border: 1px solid var(--ws-gray-200) !important;
  border-radius: 0 !important;
  box-shadow: none !important;
}
.mapa-sedes-ws .leaflet-control-zoom a {
  border-radius: 0 !important;
  color: var(--ws-black) !important;
  background-color: var(--ws-white) !important;
}
.mapa-sedes-ws .leaflet-control-zoom a:hover {
  background-color: var(--ws-gray-100) !important;
}
.mapa-sedes-ws .leaflet-popup-content-wrapper,
.mapa-sedes-ws .leaflet-popup-tip {
  border-radius: 0;
  box-shadow: 0 12px 28px -14px rgba(0,0,0,.35);
}
.mapa-sedes-ws .leaflet-popup-content {
  font-family: inherit;
  color: var(--ws-black);
  margin: .85rem 1rem;
}
/* Marcador propio en negro (ver script en index.php, usa L.divIcon con
   esta clase) en vez del pin azul por defecto de Leaflet. */
.ws-map-pin {
  width: 26px;
  height: 26px;
  filter: drop-shadow(0 3px 4px rgba(0,0,0,.35));
}
.badge-ciudad {
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: .7rem;
  border: 1px solid var(--ws-gray-400);
  padding: .3rem .7rem;
  color: var(--ws-gray-600);
}

/* ---------- Blog ---------- */
.card-post { transition: transform .3s ease, box-shadow .3s ease; }
.card-post:hover { transform: translateY(-6px); box-shadow: 0 22px 42px -22px rgba(0,0,0,.25); }
.card-post img { aspect-ratio: 4/3; object-fit: cover; width: 100%; }
.card-post .fecha { font-size: .8rem; color: var(--ws-gray-600); }

/* ---------- Footer ---------- */
.footer-ws {
  background-color: var(--ws-black);
  color: var(--ws-gray-400);
}
.footer-ws a { color: var(--ws-white); text-decoration: none; }
.footer-ws a:hover { color: var(--ws-gray-400); }
.footer-ws hr { border-color: var(--ws-gray-800); }

/* ---------- Placeholder de imagen (mientras llegan fotos reales) ---------- */
.img-placeholder {
  position: relative;
  overflow: hidden;
  background: repeating-linear-gradient(135deg, var(--ws-gray-200), var(--ws-gray-200) 10px, var(--ws-gray-100) 10px, var(--ws-gray-100) 20px);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ws-gray-600);
  font-size: .8rem;
  text-align: center;
  padding: 1rem;
}
.img-placeholder::after {
  content: "";
  position: absolute;
  top: 0;
  left: -150%;
  width: 55%;
  height: 100%;
  background: linear-gradient(120deg, transparent, rgba(255,255,255,.55), transparent);
  animation: ws-shimmer 3.4s ease-in-out infinite;
}

/* ---------- Wrapper para foto/placeholder con zoom al pasar el mouse ----------
   Cuando reemplaces un placeholder por la foto real, solo pon la <img>
   dentro de un <div class="img-wrap"> y el zoom queda funcionando solo.
   La foto real también se pasa a blanco y negro por CSS (03-sep-2026,
   primera foto real del sitio: assets/img/blog/kerafort.jpg), igual que
   cada video y mapa, para no romper la paleta de la marca. */
.img-wrap { overflow: hidden; }
.img-wrap img,
.img-wrap .img-placeholder,
.img-wrap video { transition: transform .6s ease; }
.img-wrap img { filter: grayscale(1) contrast(1.05) brightness(.95); }
.img-wrap:hover img,
.img-wrap:hover .img-placeholder,
.img-wrap:hover video { transform: scale(1.05); }

/* ---------- Video real en vez de foto (servicios.php) ----------
   Cada sección de servicio (Peluquería/Uñas/Cosmetología) usa el mismo
   video ya recortado para las tarjetas del home, esta vez en un recuadro
   4:3. Misma degradación elegante que el resto del sitio: si el video no
   carga, se queda el placeholder de foto pendiente, sin romper nada. */
.servicio-media {
  position: relative;
  aspect-ratio: 4/3;
}
.servicio-media__placeholder.img-placeholder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
/* De lg para arriba, texto e imagen van uno al lado del otro en la misma
   fila (ver servicios.php: la fila ya no fuerza align-items-center, así
   que las dos columnas se estiran a la misma altura por defecto). Aquí el
   recuadro de video/foto suelta su proporción fija 4:3 y se estira a esa
   misma altura del texto, para que ambas "cajas" queden simétricas y con
   los mismos márgenes arriba/abajo en vez de una más alta que la otra. */
@media (min-width: 992px) {
  .servicio-media {
    aspect-ratio: auto;
    height: 100%;
    min-height: 360px;
  }
}
.servicio-media__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 1;
  filter: grayscale(1) contrast(1.05) brightness(.95);
  opacity: 0;
  transition: opacity .8s ease, transform .6s ease;
  pointer-events: none;
}
.servicio-media.has-video .servicio-media__video { opacity: 1; }

/* =========================================================
   DINAMISMO: scroll, reveals, ticker y contadores
   ========================================================= */

/* Barra fina de progreso de scroll, fija arriba de todo */
.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 3px;
  width: 0%;
  background-color: var(--ws-black);
  z-index: 1050;
}

/* Elementos que aparecen suavemente al entrar en pantalla (ver script.js) */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity .7s ease, transform .7s ease;
  transition-delay: calc(var(--ws-delay, 0) * 90ms);
}
.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* Tarjetas de estadísticas (6 sedes / 2 ciudades / 3 especialidades).
   height:100% + flex centrado: si una etiqueta es más larga que las
   otras y se parte en dos líneas, el cuadro sigue ocupando el mismo
   alto que sus vecinos en vez de quedar más chico o descuadrado. */
.stat-box {
  border: 1px solid var(--ws-gray-200);
  padding: 2rem 1rem;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  transition: transform .3s ease, box-shadow .3s ease, border-color .3s ease;
}
.stat-box:hover {
  transform: translateY(-4px);
  box-shadow: 0 20px 36px -22px rgba(0,0,0,.28);
  border-color: var(--ws-black);
}
.stat-box .display-6 { font-variant-numeric: tabular-nums; }

/* Cinta de texto en movimiento continuo (marquee) */
.ws-ticker {
  overflow: hidden;
  background-color: var(--ws-black);
  border-top: 1px solid var(--ws-gray-800);
  border-bottom: 1px solid var(--ws-gray-800);
  white-space: nowrap;
}
.ws-ticker__track {
  display: inline-flex;
  align-items: center;
  gap: 2.5rem;
  padding-block: .9rem;
  animation: ws-scroll 26s linear infinite;
  will-change: transform;
}
.ws-ticker__track span {
  display: inline-flex;
  align-items: center;
  gap: 2.5rem;
  text-transform: uppercase;
  letter-spacing: .18em;
  font-size: .78rem;
  font-weight: 600;
  color: var(--ws-gray-400);
}
.ws-ticker__track span i { font-size: .5rem; color: var(--ws-gray-600); }

/* Slider de logos de marcas oficiales (Wella, Yellow, Schwarzkopf...).
   Misma mecánica de loop infinito que el ticker: dos grupos idénticos uno
   detrás del otro, y la animación desplaza exactamente el 50% del ancho
   total (= el ancho de un solo grupo), así el loop no se nota.
   En blanco y negro por defecto para respetar la paleta; a color y con
   una leve elevación al pasar el mouse sobre cada logo puntual. */
.ws-brands {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}
.ws-brands__track {
  display: inline-flex;
  align-items: center;
  gap: 4.5rem;
  animation: ws-scroll 34s linear infinite;
  will-change: transform;
}
.ws-brands:hover .ws-brands__track { animation-play-state: paused; }
.ws-brands__set {
  display: inline-flex;
  align-items: center;
  gap: 4.5rem;
}
.ws-brands__set img {
  height: 64px;
  width: auto;
  filter: grayscale(1);
  opacity: .5;
  transition: filter .35s ease, opacity .35s ease, transform .35s cubic-bezier(.22,1,.36,1);
}
.ws-brands__set img:hover {
  filter: grayscale(0) drop-shadow(0 10px 18px rgba(0,0,0,.18));
  opacity: 1;
  transform: translateY(-4px) scale(1.08);
}

/* ---------- Keyframes ---------- */
@keyframes ws-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@keyframes ws-aurora {
  0%   { transform: translate(0, 0) scale(1); }
  100% { transform: translate(-3%, 2%) scale(1.08); }
}
@keyframes ws-fade-up {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes ws-shimmer {
  0%       { left: -150%; }
  60%,100% { left: 150%; }
}

/* Respeta a quien prefiere menos movimiento en pantalla */
@media (prefers-reduced-motion: reduce) {
  .ws-ticker__track,
  .ws-brands__track,
  .hero-aurora,
  .img-placeholder::after,
  .hero-content .eyebrow,
  .hero-content h1,
  .hero-content .lead,
  .hero-content .d-flex {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
