/*
  La página pública de un salón, y la de reservar.

  Se monta SOBRE `kabelo.css`, que es el sistema de diseño del producto: sus
  tokens `--k-*`, su tipografía, su escala de espacio y su modo oscuro. La
  primera versión de este archivo se inventó tokens propios —`--marca`,
  `--fondo`, `--radio`, una serif distinta— y con eso la cara pública del
  producto dejó de parecerse al producto. Un sistema de diseño que sólo cumple
  la consola no es un sistema de diseño.

  Lo único propio de aquí es lo que la consola no tiene: una portada, una
  franja de datos y una carta de precios. Todo lo demás sale de los tokens.

  El color del negocio SOBRESCRIBE `--k-brand` al cargar, así que el botón
  primario, los enlaces y los realces se tiñen solos sin reglas nuevas.
*/

/* --- Cimientos -------------------------------------------------------------- */

* { box-sizing: border-box; }

/*
  `hidden` gana siempre.

  Es un atributo, y el navegador lo aplica con una regla de la más baja
  prioridad: cualquier `display` propio lo pisa. Sin esto, el botón de reservar
  seguía visible en un salón sin agenda —tenía `display: inline-flex` por ser
  botón— y llevaba a una pantalla que no existe.
*/
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--k-bg);
  color: var(--k-text);
  font: var(--k-body-lg) var(--k-font-sans);
}

/*
  Los títulos, en la tipografía de TÍTULOS del sistema.

  Aquí hubo una serif inventada —Georgia a pelo— buscando "aire de salón", y se
  quitó: meter una familia que nadie eligió es lo mismo que inventarse los
  colores. Ahora la serif existe, se llama `--k-font-display`, la eligió Héctor
  y vive en `kabelo.css` con su archivo propio. Entra por la puerta.

  `h1` y `h2` la usan porque son los que dan el tono de la página. `h3` NO: es
  el nombre de un servicio dentro de una tarjeta, va junto a su precio y su
  duración, y ahí lo que hace falta es leer rápido. Una serif pequeña al lado de
  un precio en sans se ve como un descuido, no como un detalle.
*/
h1, h2, h3 {
  font-weight: 600;
  color: var(--k-text);
}
h1, h2 {
  font-family: var(--k-font-display);
  /* La serif ya trae su propio ritmo: el `-.02em` que aprieta la sans le come
     el aire entre astas y la vuelve una mancha. */
  letter-spacing: -.01em;
}
h3 { font-family: var(--k-font-sans); letter-spacing: -.02em; }

/* Más grande que antes: una serif con este peso aguanta el tamaño, y el
   titular es lo único que se lee entero en la primera pantalla. */
h1 { margin: 0; font-size: clamp(2.4rem, 6.5vw, 4rem); line-height: 1.02; }
h2 { margin: 0 0 var(--k-space-5); font-size: clamp(1.7rem, 4vw, 2.5rem); line-height: 1.12; }
h3 { margin: 0 0 var(--k-space-1); font-size: var(--k-h3); }
.sub { margin: 0 0 var(--k-space-4); color: var(--k-text-muted); }

.cargando {
  max-width: 640px;
  margin: 0 auto;
  padding: var(--k-space-20) var(--k-space-6);
  text-align: center;
  color: var(--k-text-muted);
}

/* --- La barra --------------------------------------------------------------- */

/*
  Pegada arriba, con la acción siempre a la vista: quien bajó hasta el equipo y
  se decidió no tiene que volver a subir a buscar el botón.
*/
.barra {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: var(--k-space-4);
  min-height: var(--k-header);
  padding: var(--k-space-2) clamp(var(--k-space-4), 4vw, var(--k-space-10));
  background: color-mix(in srgb, var(--k-surface) 92%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--k-border);
}
.barra-marca {
  display: flex;
  align-items: center;
  gap: var(--k-space-3);
  margin-right: auto;
  /*
    Que pueda encogerse.

    Sin esto, un nombre largo —"FMNavarro Hair Studio", que no es raro— empujaba
    el resto de la barra fuera de la pantalla: en un teléfono de 360px el botón
    de reservar quedaba 306 píxeles a la derecha del borde. No es que estuviera
    apretado: no se veía, y la página se movía de lado.

    El clásico de flexbox: un elemento no encoge por debajo de su contenido a
    menos que se le diga.
  */
  min-width: 0;
  color: inherit;
  text-decoration: none;
  font-family: var(--k-font-sans);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.barra-marca img, .barra-marca .inicial {
  width: 38px; height: 38px;
  border-radius: var(--k-radius-md);
  object-fit: cover;
  /* El logo NO encoge.  en la marca dejaba que se comprimiera
     hasta desaparecer, y la barra se quedaba sin la marca del negocio — que es
     justo lo que la barra existe para enseñar. */
  flex: none;
}
/* Sólo el TEXTO cede: se recorta con puntos suspensivos antes que empujar el
   botón fuera de la pantalla. */
.barra-marca > span:last-child {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.barra-nav { display: flex; gap: var(--k-space-6); }
.barra-nav a { color: var(--k-text-muted); text-decoration: none; font: var(--k-body); }
.barra-nav a:hover { color: var(--k-text); }

@media (max-width: 720px) {
  /*
    En un teléfono la barra guarda sólo lo que se usa.

    Los enlaces de SECCIÓN se van: están a un dedo hacia abajo, y un menú
    desplegable para tres es un toque de más. Pero "Mi cuenta" NO es una
    sección de esta página: es la entrada a lo suyo —sus citas, sus puntos, sus
    pruebas— y no está en ninguna parte de la landing. Escondiéndolo entero, en
    un teléfono no había forma de llegar.
  */
  .barra-nav a:not(.barra-cuenta) { display: none; }
  .barra-nav { gap: var(--k-space-3); }
  .barra-marca { font-size: 1rem; }

  /* Todo un poco más junto: en 360px —el ancho de un teléfono barato, que es
     con lo que entra media clientela— el botón de reservar se salía doce
     píxeles por la derecha. Doce píxeles es que no está. */
  .barra { gap: var(--k-space-2); padding-left: var(--k-space-3); padding-right: var(--k-space-3); }
  .barra .boton.chico { padding: 0 var(--k-space-4); }
  .barra-marca img, .barra-marca .inicial { width: 32px; height: 32px; }
  /*
    En el teléfono más estrecho, el nombre se va y se queda el logo.

    Recortado con puntos suspensivos, "FMNavarro Hair St…" ocupa el sitio del
    botón sin decir nada que el logo no diga ya. El logo identifica solo, y
    sigue llevando al inicio.
  */
  .barra-marca > span:last-child { display: none; }
  /* El botón no se parte en dos renglones: un botón de dos líneas en una barra
     de cincuenta píxeles se lee como algo roto. */
  .barra .boton { white-space: nowrap; }
}

.inicial {
  display: grid;
  place-items: center;
  background: var(--k-brand);
  color: var(--k-on-brand, #fff);
  font-family: var(--k-font-sans);
  font-weight: 700;
}

/*
  El margen de la página, en un solo sitio.

  Cada bloque calculaba el suyo —la portada con un `clamp`, la franja con otro
  ancho máximo, las secciones con un tercero— y el resultado era que el titular
  arrancaba en 48px, la franja en 122 y los títulos de sección en 52. Tres
  márgenes distintos en la misma página se ven aunque no se sepa por qué: es
  exactamente lo que hace que algo parezca maquetado a mano.

  `--k-columna` es el ancho útil y `--k-aire` el respiro contra el borde en las
  pantallas estrechas. `--k-margen` es lo que hay que separar del borde de la
  ventana para caer en la primera columna, y lo usan TODOS: la portada, la
  franja, las secciones, el cierre y el pie.

  `max(...)` y no una resta a secas: por debajo de 1240px la resta se vuelve
  negativa y el contenido se saldría de la pantalla.
*/
#salon, #reserva, #panel, #entrar, #listo, #sinsalon {
  --k-columna: 1240px;
  --k-aire: clamp(var(--k-space-5), 4vw, var(--k-space-8));
  --k-margen: max(var(--k-aire), calc((100% - var(--k-columna)) / 2 + var(--k-aire)));
}

/* --- La portada ------------------------------------------------------------- */

.portada {
  /* El respiro vertical, con nombre: la foto de al lado tiene que poder
     cancelarlo para llegar al borde de la sección. Ver `.portada.con-foto`. */
  --k-portada-aire: clamp(var(--k-space-10), 6vw, var(--k-space-16));

  display: grid;
  align-items: center;
  min-height: min(58vh, 460px);
  /* El texto arranca en la primera columna, igual que todos los títulos de
     sección: es lo que hace que la página se lea como una rejilla y no como
     bloques apilados cada uno por su cuenta. */
  padding: var(--k-portada-aire) var(--k-margen);
  /* Los DOS colores del negocio. Antes el acento se guardaba, se pintaba en el
     panel y en la landing no hacía nada: cambiarlo no cambiaba nada, que es la
     peor clase de ajuste. Aquí es el segundo tono del degradado, y con eso la
     portada de dos salones distintos deja de parecerse. */
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--k-brand) 14%, var(--k-bg)) 0%,
    var(--k-bg) 55%,
    color-mix(in srgb, var(--k-accent) 12%, var(--k-bg)) 100%);
}

/*
  En el TELÉFONO, el texto va encima de la foto y sobre un velo.

  El velo no es decoración: la foto la sube el salón y no hay forma de saber si
  es clara, así que sin él un titular blanco sobre una pared blanca desaparece.
  Se degrada hacia la derecha para que la foto se siga viendo.
*/
.portada.con-foto {
  background:
    linear-gradient(90deg,
      rgba(13, 27, 32, .86) 0%, rgba(13, 27, 32, .55) 55%, rgba(13, 27, 32, .25) 100%),
    /* Encuadrada por arriba: una portada apaisada recortada al centro se come
       la parte de arriba, que es donde suele estar el rótulo del local. */
    var(--foto) center top / cover no-repeat;
}
.portada.con-foto h1,
.portada.con-foto .lema { color: #fff; }

/*
  En pantalla ancha, la portada se PARTE: el texto a un lado, la foto al otro.

  Encima de la foto, el titular necesita un velo que oscurece la foto entera —
  la mitad del sitio se gasta en tapar lo único que el salón subió para
  enseñar. Al lado, ni el texto pelea con la imagen ni la imagen se apaga: el
  titular se lee sobre el fondo de la página, negro sobre claro, y la foto se
  ve tal como es.

  Es también lo que da a la página su primer renglón de rejilla. Todo lo que
  viene debajo arranca del mismo margen izquierdo que este titular.
*/
@media (min-width: 900px) {
  .portada.con-foto {
    /* La foto un poco más ancha que el texto: es la que tiene que dominar la
       primera pantalla. */
    grid-template-columns: minmax(0, 1fr) 1.15fr;
    align-items: center;
    gap: clamp(var(--k-space-8), 5vw, var(--k-space-16));
    /* Fuera el velo y el fondo: la foto pasa a ser una columna, no un tapiz. */
    background: var(--k-bg);
    /* La foto llega AL BORDE de la ventana. Cortada por el borde se lee como
       una ventana a la que le sigue algo; con un margen a la derecha se lee
       como una imagen pegada en una diapositiva. */
    padding-right: 0;
    overflow: hidden;
  }
  .portada.con-foto::after {
    content: '';
    display: block;
    align-self: stretch;
    /* Sin alto propio se colapsaría: la fila la marca el texto, y una columna
       vacía a su lado no mide nada. */
    min-height: min(58vh, 460px);
    /*
      Y CANCELA el respiro vertical de la portada para llegar a los dos bordes.

      Sin esto la foto terminaba ochenta píxeles antes del final de la sección,
      la franja se montaba sobre ese hueco vacío en vez de sobre la foto, y
      entre las dos quedaba una franja de fondo que hacía que la portada y los
      datos se leyeran como dos bloques sueltos. Héctor lo dijo tal cual: "el
      encabezado, no sé por qué ya no se ve la franja, estaba mejor".
    */
    margin-block: calc(var(--k-portada-aire) * -1);
    /*
      Y se FUNDE con el texto por la izquierda, en vez de cortarse en seco.

      Sin esto la foto empieza con una línea recta a media pantalla, y la
      portada se lee como dos bloques pegados: un texto y una foto. Con el
      degradado el fondo de la página entra en la foto y las dos partes pasan a
      ser una sola imagen — que es lo que hace la maqueta.

      Es también lo que salva a una portada pequeña: el borde izquierdo, que es
      donde el ojo compara la foto con el texto nítido de al lado, queda
      cubierto por el fondo.

      El degradado va PRIMERO porque las capas de fondo se apilan de arriba
      abajo: escrito después quedaría debajo de la foto y no se vería.
    */
    background:
      linear-gradient(90deg,
        var(--k-bg) 0%,
        color-mix(in srgb, var(--k-bg) 55%, transparent) 12%,
        transparent 30%),
      var(--foto) center top / cover no-repeat;
  }

  /*
    Una foto pequeña NO cambia el diseño de la portada.

    Se probaron las dos salidas y las dos son peores que el problema. Con
    `contain` queda una banda delgada con aire muerto arriba y abajo. Con la
    foto reducida a su propio tamaño, queda una tarjeta flotando en medio de la
    sección, sin llegar a los bordes — Héctor lo señaló con dos flechas: "debe
    ocupar el alto completo de la sección".

    Y las dos comparten el mismo error de fondo: deformar el diseño de todos
    para tapar un archivo que no da la talla. La portada llena su columna
    SIEMPRE, y a quien subió una foto pequeña se le dice en el panel, que es
    donde puede cambiarla.
  */
  /* Sobre el fondo de la página, el blanco desaparece: vuelve el color normal
     del texto, que es lo que se lee. */
  .portada.con-foto h1 { color: var(--k-text); }
  .portada.con-foto .lema { color: var(--k-text-muted); }
}

.portada-texto { max-width: 620px; }
.lema { margin: var(--k-space-3) 0 0; font-size: 1.05rem; color: var(--k-text-muted); }

.acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--k-space-2);
  margin-top: var(--k-space-8);
}

/* --- Botones ---------------------------------------------------------------- */

.boton {
  /* La altura de control del sistema: esto se toca con el pulgar, de pie. */
  min-height: var(--k-control);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--k-space-2);
  padding: 0 var(--k-space-6);
  /*
    Con el borde de CONTROL y una sombra.

    Llevaba `--k-border`, que es el filete de separar tarjetas: sobre el fondo
    crema daba 1.07 de contraste y el botón secundario se veía como una palabra
    flotando, no como algo que se toca. La sombra hace el resto — un control
    tiene que parecer que está por encima de la página, no impreso en ella.
  */
  border: 1px solid var(--k-border-control);
  border-radius: var(--k-radius-pill);
  background: var(--k-surface);
  color: var(--k-text);
  box-shadow: var(--k-shadow-sm);
  font: 600 var(--k-button) var(--k-font-sans);
  text-decoration: none;
  transition: transform var(--k-fast), box-shadow var(--k-fast), background var(--k-fast);
}
/* Al pasar por encima sube: el movimiento y la sombra juntos son lo que dice
   "esto responde". Sólo uno de los dos se lee como un parpadeo. */
.boton:hover { transform: translateY(-1px); box-shadow: var(--k-shadow-drop); }
@media (prefers-reduced-motion: reduce) {
  .boton { transition: box-shadow var(--k-fast); }
  .boton:hover { transform: none; }
}
/*
  La tinta de encima se CALCULA, no se supone.

  Estaba en `#fff` fijo, y con el azul petróleo de fábrica se lee perfecto. Pero
  el color lo elige el negocio, y un crema, un amarillo o un rosa pálido dejaban
  el texto blanco sobre su propio color: el botón se veía vacío.

  `--k-on-brand` la pone `assets/color.js` al aplicar la marca, midiendo cuál de
  las dos —blanca o negra— contrasta más con ese color. El respaldo mantiene el
  comportamiento de siempre si el guion no llegó o el negocio no puso color.
*/
.boton.principal {
  background: var(--k-brand);
  border-color: var(--k-brand);
  color: var(--k-on-brand, #fff);
  box-shadow: var(--k-shadow-drop);
}
.boton.chico { min-height: var(--k-btn-md); padding: 0 var(--k-space-5); }
.boton.grande { min-height: 56px; width: 100%; font-size: 1rem; }
.boton svg { width: 18px; height: 18px; flex: none; }

/* --- La franja de datos ----------------------------------------------------- */

/*
  Lo que se pregunta antes de ir, en una línea: a qué hora abren, dónde están y
  cómo escribirles. Montada sobre la portada porque es la continuación de la
  primera impresión, no una sección aparte.
*/
.franja {
  /*
    Rejilla y no fila.

    Con la calificación y el mapa dentro son seis datos, y en fila se partían en
    cuatro arriba y dos abajo, cada uno de un ancho distinto y sin alinear con
    los de encima. Se leía como un renglón que se desbordó.

    En rejilla, las columnas caen unas debajo de otras: dos filas de cuatro y
    de dos siguen siendo dos filas, pero se leen como una tabla. Y en un
    teléfono cada dato ocupa su renglón entero, que es lo que hace que se
    puedan tocar los dos que son ligas.
  */
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: var(--k-space-4) var(--k-space-6);
  /*
    Lo que se alinea de una tarjeta es su BORDE, no su texto.

    La franja es una tarjeta: su contenido lleva relleno propio, igual que el
    nombre dentro de una tarjeta de servicio. Lo que tiene que caer en la
    columna es el borde, a la misma altura que el borde de la primera tarjeta
    de la carta y que el título de la sección de arriba.

    Y se consigue con el MARGEN, no con un ancho máximo: `--k-margen` ya es la
    distancia exacta desde el borde de la ventana hasta la columna, en las dos
    direcciones y en cualquier tamaño de pantalla. Con `max-width` y `auto`
    hacen falta dos cuentas que tienen que dar lo mismo, y no daban.
  */
  /* Bien montada sobre la portada: `-32px` la dejaba a medio camino, con un
     dedo de fondo asomando entre la foto y la tarjeta. */
  margin: calc(var(--k-space-12) * -1) var(--k-margen) 0;
  padding: var(--k-space-5);
  position: relative;
  background: var(--k-surface-raised);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
  box-shadow: var(--k-shadow-md);
}
.franja:empty { display: none; }
.dato { display: flex; align-items: center; gap: var(--k-space-3); }
/* En la variante legible: `--k-brand` a secas es el color que el negocio eligió
   mirando la pantalla clara, y sobre el fondo oscuro estos iconos quedaban del
   mismo tono que la tarjeta. */
.dato svg { width: 22px; height: 22px; flex: none; color: var(--k-brand-texto); }
.dato .k {
  display: block;
  font: var(--k-small) var(--k-font-sans);
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--k-text-muted);
}
.dato .v { font-weight: 600; }

/*
  La estrella de la calificación va RELLENA y en el acento.

  Los demás iconos de la franja son de trazo porque son etiquetas: dicen de qué
  habla el dato que está al lado. Éste no es una etiqueta, es la nota — y una
  estrella hueca junto a un 4.9 se lee como una estrella vacía, que es justo lo
  contrario de lo que dice el número.
*/
.dato .icono-estrella {
  fill: var(--k-accent-texto);
  stroke: var(--k-accent-texto);
}

@media (max-width: 900px) {
  /*
    En el teléfono la franja deja de montarse sobre la portada.

    Superpuesta necesita que la portada le deje sitio debajo, y en una pantalla
    estrecha la franja crece a cuatro renglones: se comía el botón de reservar.
    Aquí baja y se apoya, sin margen negativo.

    El margen lateral es el MISMO `--k-margen` de siempre, sólo se repite para
    poder cambiar el de arriba. Tenía uno propio de 16px y era el único trozo
    de la página que no caía en la columna.
  */
  .franja { margin: 0 var(--k-margen); }
}

/* --- Los servicios de la reserva, por categoría ------------------------------ */

/*
  Agrupados, con el nombre del grupo encima.

  Eran veinte casillas seguidas. Agrupadas se busca por donde uno ya piensa, y
  es lo que hace que el atajo de la portada signifique algo.
*/
.grupo { display: contents; }
.grupo-titulo {
  /* De borde a borde de la rejilla: un encabezado que ocupa una celda se lee
     como una opción más, no como el título de las que vienen debajo. */
  grid-column: 1 / -1;
  margin: var(--k-space-4) 0 0;
  font: var(--k-small) var(--k-font-sans);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--k-text-muted);
}
.grupo:first-child .grupo-titulo { margin-top: 0; }

/*
  El grupo por el que se llegó, señalado.

  Se marca el TÍTULO y no las casillas: teñir cinco opciones se lee como que ya
  están elegidas, y no lo están — elegir por alguien es decidirle la cita.
*/
.grupo.destacado .grupo-titulo { color: var(--k-accent-texto); }
.grupo.destacado .grupo-titulo::after {
  content: '· lo que buscabas';
  margin-left: var(--k-space-2);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
}

/* --- El video, como tarjeta ---------------------------------------------------
   Nunca un reproductor incrustado: eso traería los scripts y las cookies de
   otro dominio a la página de un salón. Ver `src/video.js`. */

.video-tarjeta {
  display: grid;
  grid-template-columns: 1fr;
  align-items: stretch;
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
  overflow: hidden;
  background: var(--k-surface);
  color: inherit;
  text-decoration: none;
  transition: transform var(--k-fast), box-shadow var(--k-fast);
}
.video-tarjeta:hover {
  transform: translateY(-2px);
  box-shadow: var(--k-shadow-md);
}
@media (prefers-reduced-motion: reduce) {
  .video-tarjeta { transition: none; }
  .video-tarjeta:hover { transform: none; }
}
@media (min-width: 720px) {
  /* La portada manda: es lo que hace que alguien quiera tocarla. */
  .video-tarjeta { grid-template-columns: 1.6fr 1fr; }
}

.video-portada {
  display: block;
  aspect-ratio: 16 / 9;
  /* Sin portada, el color del negocio y no un gris: sigue siendo su tarjeta
     aunque no haya subido la imagen. */
  background: linear-gradient(120deg,
    color-mix(in srgb, var(--k-brand) 80%, #000),
    color-mix(in srgb, var(--k-brand) 45%, #000));
}
.video-portada.con-foto { background: var(--foto) center / cover no-repeat; }

.video-texto {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--k-space-1);
  padding: var(--k-space-6);
}
.video-texto strong { font-size: 1.1rem; }
.video-texto .sub { margin: 0; }

/* El triángulo, que es lo que dice "esto es un video" sin escribirlo. */
.video-play {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-bottom: var(--k-space-3);
  border-radius: var(--k-radius-pill);
  background: var(--k-brand);
  color: var(--k-on-brand, #fff);
}
.video-play svg { width: 22px; height: 22px; }

/* --- El atajo a la reserva --------------------------------------------------- */

/*
  Debajo de la franja, y con la misma forma de tarjeta ancha.

  Quien llega por un código QR viene a ver si puede sentarse, no a leer. Esto
  abre la pantalla de reservar con la categoría puesta, en vez de en blanco.
*/
.atajo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--k-space-5);
  margin: var(--k-space-10) var(--k-margen) 0;
  padding: var(--k-space-5);
  background: var(--k-surface);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
}
.atajo-texto {
  display: flex;
  flex-direction: column;
  gap: var(--k-space-1);
  /* Con un mínimo, para que en una pantalla intermedia el texto no se parta en
     una columna de dos letras antes de decidirse a bajar de línea. */
  flex: 1 1 12rem;
}
.atajo-texto strong { font-size: 1.15rem; }
.atajo-texto .sub { margin: 0; }

/*
  Las puertas: una rejilla, no una fila.

  En fila, una categoría con nombre largo estira su casilla y deja las otras
  tres encogidas, y eso se lee como que una importa más que las demás. En
  rejilla todas miden igual, que es lo que son: cuatro caminos equivalentes.
*/
.atajo-categorias {
  flex: 3 1 22rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
  gap: var(--k-space-2);
}
.atajo-categorias:empty { display: none; }
.puerta {
  /* El clásico de las rejillas: un hijo no encoge por debajo de su contenido a
     menos que se le diga, y con eso la casilla desborda su columna. */
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* Relleno lateral corto: lo que va dentro es un nombre de categoría, y los
     nombres los pone el negocio. "Tratamientos" no cabía y el borde le cortaba
     la última letra. Cada píxel de relleno es un píxel menos para el nombre. */
  padding: var(--k-space-3);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-md);
  background: var(--k-bg);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--k-fast), transform var(--k-fast);
}
.puerta:hover {
  border-color: var(--k-brand);
  transform: translateY(-1px);
}
.puerta strong {
  font-size: .88rem;
  /* Red de seguridad, no la solución: `break-word` sólo parte una palabra que
     de verdad no cabe de ninguna manera. Con `anywhere` partía "Tratamientos"
     en "Tratamient/os" aun cuando bastaba con estrechar el relleno, y un
     nombre partido a la mitad se lee peor que uno pequeño. */
  overflow-wrap: break-word;
}
.puerta span { color: var(--k-text-muted); font: var(--k-small) var(--k-font-sans); }

.atajo-ir {
  /* 17rem y no 13: con 13 los cuatro pasos no caben en un renglón y "Hora"
     bajaba sola a una segunda línea, descentrada bajo las otras tres. Una
     cuenta de pasos que se parte deja de leerse como una cuenta. */
  flex: 1 1 17rem;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--k-space-2);
}

/*
  Los pasos que faltan, numerados.

  "Reservar" sin saber si son dos toques o diez es lo que hace que alguien
  prefiera mandar un WhatsApp y quedarse esperando. Cuatro pasos cortos dicen
  que se acaba pronto.
*/
.atajo-pasos {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--k-space-1) var(--k-space-3);
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: paso;
  color: var(--k-text-muted);
  font: var(--k-small) var(--k-font-sans);
}
.atajo-pasos li {
  display: flex;
  align-items: center;
  gap: var(--k-space-1);
  counter-increment: paso;
}
.atajo-pasos li::before {
  content: counter(paso);
  display: grid;
  place-items: center;
  width: 1.15rem;
  height: 1.15rem;
  border-radius: var(--k-radius-pill);
  background: var(--k-border);
  color: var(--k-text);
  font-size: .68rem;
  font-weight: 600;
}

/* --- Bloques ---------------------------------------------------------------- */

/*
  El ancho de la página.

  Eran 1100px, y con cuatro tarjetas por fila la rejilla se quedaba corta: en
  una pantalla de 1280 sobraban doscientos píxeles a cada lado y el contenido
  se leía como una columna estrecha en medio de un folio. 1240 es lo que hace
  que quepan cuatro tarjetas de servicio sin apretarlas.
*/
.bloque {
  max-width: var(--k-columna);
  margin: 0 auto;
  padding: clamp(var(--k-space-10), 6vw, var(--k-space-16)) var(--k-aire);
}
/*
  El encabezado de una sección: la categoría, el título y la salida.

  Antes era un `<h2>` centrado y nada más. Centrado y solo, un título se lee
  como el de una plantilla; con la categoría encima en letra pequeña y la
  salida a la derecha, la sección se lee como parte de un estudio — y de paso
  el ojo encuentra dónde empieza cada bloque sin tener que leerlo.

  La categoría la fija el producto y el título lo escribe el negocio. Ver
  `src/secciones.js`: uno ancla la sección para quien llega de fuera, el otro
  es la voz de la casa.
*/
.cabeza-bloque {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--k-space-4);
  margin-bottom: var(--k-space-6);
}
.cabeza-texto { min-width: 0; }
.cabeza-bloque h2 { margin: 0; }
.cabeza-bloque .sub { margin: var(--k-space-2) 0 0; }

/*
  La categoría.

  Pequeña, espaciada y en el acento del negocio: tiene que leerse como una
  etiqueta y no competir con el título que va debajo. El filete corto delante
  es lo que la separa del texto normal sin necesidad de una caja.
*/
.ojal {
  display: flex;
  align-items: center;
  gap: var(--k-space-2);
  margin: 0 0 var(--k-space-2);
  font: var(--k-small) var(--k-font-sans);
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--k-accent-texto);
}
.ojal::before {
  content: '';
  width: 22px;
  height: 2px;
  flex: none;
  background: currentColor;
  border-radius: 2px;
}

/*
  La salida de la sección: "ver todo".

  Un enlace y no un botón a la vista: lo que hace es enseñar el resto de lo que
  ya está en la página, no ejecutar nada. Va al final del renglón del título
  porque ahí es donde se busca cuando la lista se acaba y falta algo.
*/
.ver-todo {
  flex: none;
  align-self: flex-end;
  padding: 0;
  border: 0;
  background: none;
  color: var(--k-brand-texto);
  font: var(--k-body) var(--k-font-sans);
  font-weight: 600;
  cursor: pointer;
}
.ver-todo:hover { color: var(--k-accent-texto); }
.ver-todo span { display: inline-block; transition: transform .15s ease; }
.ver-todo:hover span { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) {
  .ver-todo span { transition: none; }
}

/*
  La carta, en tarjetas y no en lista.

  Una lista con puntitos parece un menú de comida rápida. Cada servicio en su
  tarjeta, con el precio grande, es lo que un salón cuelga en la pared.
*/
.cartas {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: var(--k-space-4);
  /*
    Todas del mismo alto, con una condición.

    Antes iban con `align-items: start` porque estirarlas dejaba a las que no
    tienen foto con un rectángulo vacío debajo del precio. Pero escalonadas
    tampoco se veía bien: una carta con dos fotos y tres sin ellas quedaba
    dentada, y eso fue lo que Héctor enseñó en su captura.

    La salida no es elegir entre las dos: es que la que no tiene foto tampoco
    tenga hueco. Abajo, `.cartas:has(.con-foto) .carta:not(.con-foto)::before` le
    pone un marcador del mismo alto — así todas miden igual, nadie tiene un
    agujero, y se nota qué servicio le falta la foto.

    Sólo cuando ALGUNA tiene foto: en una carta sin ninguna, un marcador en cada
    tarjeta sería adorno repetido cinco veces.
  */
  align-items: stretch;
}
.cartas:not(:has(.con-foto)) { align-items: start; }

/*
  La lista recortada enseña sólo la primera fila.

  Se esconde con CSS y no quitando las tarjetas del HTML: así "ver todo" no
  tiene que volver a pintar nada —no hay parpadeo, no se pierde el sitio— y
  quien llega con el buscador del navegador encuentra igual el servicio que
  buscaba.

  `:nth-child(n + 5)` y no un número calculado: cuatro es lo que llena una fila
  en esta rejilla, y el día que la rejilla cambie este número cambia con ella.
*/
.cartas[data-recortado] > :nth-child(n + 5) { display: none; }
.carta {
  display: flex;
  flex-direction: column;
  padding: var(--k-space-5);
  background: var(--k-surface);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
}
/*
  Con foto, la tarjeta se abre por arriba.

  La imagen va a sangre —de borde a borde— y no metida entre los márgenes: una
  foto con un marco de veinte píxeles alrededor parece un adorno, y ésta es
  información. El servicio sin foto conserva su tarjeta entera, con su padding.
*/
.carta.con-foto { padding: 0; overflow: hidden; }
/*
  Todo lleva márgenes laterales MENOS la foto.

  Decía `:not(.carta-foto)`, y dejó de acertar el día que la foto pasó a ir
  envuelta en un botón para poder abrirse en grande: el hijo directo pasó a ser
  `.carta-lupa` y se llevaba los márgenes, así que la imagen quedaba pegada
  arriba pero con un borde blanco a los lados. Ése es el "está al ras y se ve
  feo" de la captura.
*/
.carta.con-foto > *:not(.carta-foto):not(.carta-lupa) {
  padding-left: var(--k-space-5);
  padding-right: var(--k-space-5);
}
.carta.con-foto h3 { margin-top: var(--k-space-4); }
.carta.con-foto .enlace { padding-bottom: var(--k-space-5); }
/*
  El hueco de la que no tiene foto, cuando sus vecinas sí.

  Del mismo alto que una foto —misma proporción 4:3— para que la fila entera
  quede pareja, y con la tijera de la marca en un fondo suave para que se lea
  como "aquí va una foto" y no como un error de carga. Al negocio le señala,
  sin decírselo, qué servicio le falta retratar.
*/
.cartas:has(.con-foto) .carta:not(.con-foto) { padding: 0; overflow: hidden; }
.cartas:has(.con-foto) .carta:not(.con-foto) > * {
  padding-left: var(--k-space-5);
  padding-right: var(--k-space-5);
}
.cartas:has(.con-foto) .carta:not(.con-foto) > h3 { margin-top: var(--k-space-4); }
.cartas:has(.con-foto) .carta:not(.con-foto) > .enlace { padding-bottom: var(--k-space-5); }
.cartas:has(.con-foto) .carta:not(.con-foto)::before,
.promos-publicas:has(.con-foto) .promo-publica:not(.con-foto)::before {
  content: '';
  display: block;
  aspect-ratio: 4 / 3;
  /* La tijera PRIMERO: en `background`, la capa inicial se pinta encima. Con el
     degradado delante quedaba tapada y sólo se veía el color. */
  background:
    /* La tijera, incrustada: son 250 bytes y evita una petición más por una
       decoración. Al ir en la hoja se cachea con ella, y `currentColor` no
       sirve dentro de un data URI, así que el trazo va escrito. */
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239a9a9a' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='6' cy='6' r='3'/%3E%3Ccircle cx='6' cy='18' r='3'/%3E%3Cpath d='M20 4 8.12 15.88M14.47 14.48 20 20M8.12 8.12 12 12'/%3E%3C/svg%3E")
      center / 34px no-repeat,
    linear-gradient(135deg,
      color-mix(in srgb, var(--k-brand) 7%, var(--k-surface)),
      color-mix(in srgb, var(--k-accent) 9%, var(--k-surface)));
  opacity: .9;
}

.carta-foto {
  display: block;
  width: 100%;
  /* Apaisada 4:3: una foto de un corte se toma de frente, y un cuadrado le
     recorta el pelo por los lados, que es justo lo que se quiere enseñar. */
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.carta .precio {
  margin: var(--k-space-2) 0 0;
  font-family: var(--k-font-sans);
  font-size: 1.5rem;
}
/*
  "Consúltanos" ocupa el lugar del precio, pero no grita como un precio.

  Mismo renglón y mismo hueco —si desapareciera, las tarjetas de una carta a
  medio configurar quedarían de alturas distintas— con el peso de un dato
  secundario, que es lo que es: una invitación a preguntar, no una cifra.
*/
.carta .precio-sin {
  font-weight: 500;
  opacity: .75;
}

.carta .rato { margin: 0; color: var(--k-text-muted); font: var(--k-small) var(--k-font-sans); }
/*
  El enlace de acción de una tarjeta.

  Sin `.carta` delante a propósito: la tarjeta de una persona del equipo usa la
  misma clase y esta regla no la alcanzaba, así que "Reservar con Angie" salía
  con el azul subrayado que pone el navegador cuando NINGUNA regla llega. Ese
  azul no está en ningún token de este sistema, que es como se reconoce.
*/
.enlace {
  color: var(--k-brand-texto);
  font-weight: 600;
  text-decoration: none;
}
.enlace:hover { text-decoration: underline; }
.carta .enlace {
  margin-top: auto;
  padding-top: var(--k-space-4);
}

/* Quién atiende. Con la inicial y no con una silueta gris: una foto que no
   existe se nota, y una inicial sobre el color del salón no finge nada. */
/*
  El equipo, en tarjetas.

  Eran píldoras con un círculo de cuarenta píxeles: una lista de etiquetas. Y
  elegir con quién sentarse es media razón por la que alguien vuelve a un
  salón, así que la cara tiene que verse — grande y de frente.

  El retrato va AL LADO del texto y no encima: en un salón lo que se mira de
  alguien es la cara, y una tarjeta vertical con la foto arriba obliga a hacer
  la foto pequeña para que la tarjeta no mida medio metro. Al lado cabe grande.
*/
.gente {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  gap: var(--k-space-4);
}
.persona {
  display: flex;
  align-items: stretch;
  gap: var(--k-space-4);
  padding: 0;
  overflow: hidden;
  background: var(--k-surface);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
}
.persona .inicial, .persona img {
  /*
    Un retrato es más alto que ancho: en 3:4 salen la cara y los hombros, que
    es como se mira a alguien. En cuadrado se corta el pelo, y en un salón el
    pelo es justo lo que se está enseñando.

    El alto lo manda la TARJETA y no la proporción: con `aspect-ratio` la foto
    medía 144px y la tarjeta, que sólo lleva un nombre y un enlace, medía menos
    — así que quedaba un dedo de fondo debajo del retrato. El mínimo de la
    tarjeta es lo que le da sitio a la cara.
  */
  width: 108px;
  align-self: stretch;
  border-radius: 0;
  object-fit: cover;
  flex: none;
}
/* Para que la foto tenga altura que llenar: la tarjeta la marca el texto, y
   dos renglones no bastan para un retrato. */
.persona { min-height: 130px; }
.persona .inicial { font-size: 2rem; }
.persona-texto {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--k-space-1);
  padding: var(--k-space-4) var(--k-space-4) var(--k-space-4) 0;
  min-width: 0;
}
.persona-texto strong { font-size: 1.05rem; }

/*
  El puesto, debajo del nombre.

  Más pequeño y en el color secundario a propósito: quien busca a Ana lee su
  nombre, y quien busca una colorista lee esta línea. Con los dos del mismo
  peso habría que leer la tarjeta entera para saber cuál es cuál.
*/
.persona-puesto {
  font-size: .875rem;
  color: var(--k-text-muted);
  /* Sin recorte: un puesto largo se parte en dos renglones antes que salir
     cortado a media palabra, que se lee como un error. */
  line-height: 1.35;
}

/*
  El equipo como LISTA, cuando el negocio lo prefiere así.

  No es la tarjeta con la foto quitada: eso dejaría una rejilla de cajas medio
  vacías. Es otra forma — renglones a lo ancho, que es como se lee una lista de
  nombres — y por eso rompe la rejilla en vez de heredarla.

  Tiene sentido en un salón con mucha gente y pocos retratos, donde las caras
  serían iniciales grises, y en una barbería donde da igual quién te corte.
*/
.gente.equipo-lista {
  display: flex;
  flex-direction: column;
  gap: 0;
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
  overflow: hidden;
  background: var(--k-surface);
}
.persona-renglon {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--k-space-4);
  flex-wrap: wrap;
  padding: var(--k-space-4);
}
/* La raya entre renglones y no alrededor de cada uno: una lista es una cosa
   con divisiones, no una pila de cajas sueltas. */
.persona-renglon + .persona-renglon { border-top: 1px solid var(--k-border); }
.persona-renglon .persona-texto { padding: 0; }

.direccion { margin: 0 0 var(--k-space-4); font-size: 1.05rem; }

/* --- Los dos botones que flotan --------------------------------------------- */

/*
  WhatsApp abajo a la derecha y "volver arriba" encima.

  Escribir es la salida de quien no quiere reservar por la página —una duda, un
  precio, "¿tienen hueco hoy?"— y en un teléfono eso pasa a media pantalla, no
  al final. Volver arriba aparece sólo cuando hay algo de dónde volver.
*/
.flotantes {
  position: fixed;
  right: var(--k-space-4);
  bottom: var(--k-space-4);
  z-index: 20;
  display: grid;
  gap: var(--k-space-2);
  justify-items: center;
}
.flotante {
  width: 52px;
  height: 52px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--k-radius-pill);
  box-shadow: var(--k-shadow-lg);
  cursor: pointer;
  transition: transform var(--k-fast), opacity var(--k-fast);
}
.flotante:hover { transform: translateY(-2px); }
.flotante svg { width: 26px; height: 26px; }
/* El verde es de WhatsApp, no del negocio: es el color por el que se reconoce
   ese icono en cualquier pantalla del mundo, y cambiarlo lo esconde. */
.flotante.whats { background: #25d366; color: #fff; }
.flotante.arriba {
  background: var(--k-surface);
  color: var(--k-text);
  border: 1px solid var(--k-border);
  opacity: 0;
  pointer-events: none;
}
.flotante.arriba.se-ve { opacity: 1; pointer-events: auto; }

/* --- El pie ----------------------------------------------------------------- */

/*
  El contacto: el mapa a un lado y las salidas al otro.

  El mapa ocupaba el ancho entero. Para contestar "¿me queda cerca?" sobra la
  mitad, y los tres botones debajo quedaban sueltos, a media pantalla de lo que
  explicaban. Lado a lado, mirar y tocar están a la misma altura.
*/
.contacto-cuerpo {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--k-space-5);
  align-items: start;
}
@media (min-width: 860px) {
  /* El mapa manda: es lo que se mira. Los botones sólo necesitan su ancho. */
  .contacto-cuerpo { grid-template-columns: minmax(0, 1fr) 280px; }
}
.contacto-salidas {
  display: flex;
  flex-direction: column;
  gap: var(--k-space-3);
}
/* Apilados, cada uno ocupa su renglón entero: en una columna estrecha, tres
   botones de ancho automático quedan de tres tamaños distintos. */
.contacto-salidas .boton { width: 100%; }

/* --- El cierre --------------------------------------------------------------- */

/*
  La última llamada, para quien leyó hasta abajo.

  La página se acababa con tres botones y una línea de pie. Quien llega al
  final habiéndolo leído todo es quien más cerca está de reservar, y se
  encontraba con que no había nada que tocar sin volver a subir.

  Oscuro y a todo el ancho a propósito: es el punto y final, y tiene que
  leerse distinto de las secciones que vienen antes.
*/
.cierre {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--k-space-5);
  /*
    Del ancho de la columna, y con el MISMO relleno lateral que una sección.

    Llevaba un relleno propio más grande y su texto arrancaba ocho píxeles a la
    derecha de todos los títulos de la página. Es la clase de desajuste que no
    se sabe nombrar pero se ve.
  */
  /* Una tarjeta ancha, como la franja: alinea por el borde y con el mismo
     margen calculado. */
  margin: var(--k-space-16) var(--k-margen) var(--k-space-10);
  padding: clamp(var(--k-space-6), 4vw, var(--k-space-10));
  border-radius: var(--k-radius-xl);
  /* El color del negocio, oscurecido. Un negro plano sería el mismo cierre en
     todos los salones; así el punto final también es suyo. */
  background: linear-gradient(120deg,
    color-mix(in srgb, var(--k-brand) 88%, #000) 0%,
    color-mix(in srgb, var(--k-brand) 62%, #000) 100%);
  color: #f8f7f4;
}
.cierre-texto { display: flex; flex-direction: column; gap: var(--k-space-1); }
.cierre-texto strong {
  font-family: var(--k-font-display);
  font-size: 1.5rem;
  line-height: 1.15;
}
/* El `.sub` del sistema es gris sobre claro; aquí el fondo es oscuro y ese
   gris desaparece. */
.cierre-texto .sub { margin: 0; color: rgba(248, 247, 244, .75); }
.cierre .acciones { margin: 0; }
/* Sobre el fondo oscuro, el botón claro es el que se ve: el primario del
   sistema es el color del negocio, que aquí ES el fondo. */
.cierre .boton { background: rgba(255, 255, 255, .12); border-color: transparent; color: #fff; }
/* Texto oscuro FIJO: el fondo de este botón es blanco fijo, así que su letra
   no puede seguir al tema. En oscuro `--k-text` es casi blanco, y el botón
   quedaba blanco con letra blanca — invisible salvo por su sombra. */
.cierre .boton.principal { background: #fff; color: #1a1a1a; }

/* --- El pie ----------------------------------------------------------------- */

/*
  El pie, con columnas.

  Era una línea que decía "Hecho con Kabelo". Un pie así deja la página como
  cortada en seco, y desperdicia el único sitio donde se espera encontrar los
  enlaces, las redes y de quién es el negocio.
*/
/*
  El pie mide como una sección, no como el ancho de la ventana.

  Tenía el relleno en el `<footer>` y el ancho máximo en el hijo, y con eso el
  nombre del negocio arrancaba veinte píxeles a la izquierda de todos los
  títulos: el ancho máximo nunca llegaba a morder porque el relleno ya había
  descontado su parte. La franja de arriba se llevaba el mismo palo.
*/
.pie {
  border-top: 1px solid var(--k-border);
  padding: var(--k-space-12) 0 var(--k-space-8);
}
.pie-cuerpo {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--k-space-8);
  max-width: var(--k-columna);
  margin: 0 auto;
  padding: 0 var(--k-aire);
}
.pie-marca { max-width: 34ch; }
.pie-marca strong {
  display: block;
  font-family: var(--k-font-sans);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.pie-marca .sub { margin: var(--k-space-2) 0 var(--k-space-4); }

/* En columnas y no en una fila: seis enlaces seguidos en una línea se leen
   como una segunda barra de navegación, que ya está arriba. */
.pie-enlaces {
  display: grid;
  grid-template-columns: repeat(2, minmax(8rem, auto));
  gap: var(--k-space-2) var(--k-space-10);
}
.pie-enlaces a { color: var(--k-text-muted); text-decoration: none; }
.pie-enlaces a:hover { color: var(--k-text); }

.redes { display: flex; gap: var(--k-space-4); flex-wrap: wrap; }
.redes a { color: var(--k-brand-texto); font-weight: 600; text-transform: capitalize; }

.firma {
  max-width: var(--k-columna);
  margin: var(--k-space-10) auto 0;
  padding: var(--k-space-5) var(--k-aire) 0;
  border-top: 1px solid var(--k-border);
  color: var(--k-text-muted);
  font: var(--k-small) var(--k-font-sans);
}
.firma a { color: inherit; }

/* --- Reservar --------------------------------------------------------------- */

/* La página de reservar es una columna estrecha: se llena de arriba abajo y
   nada compite por la atención con el paso en el que se está. */
/*
  Reservar mide como la página del negocio.

  Estaba clavada en 640px, y en una pantalla de escritorio quedaba una columna
  estrecha en medio de un folio en blanco — con la lista de servicios apretada
  y las horas en cinco por fila cuando cabían ocho. Es la misma página, del
  mismo negocio, y tiene que verse de la misma anchura.

  El aviso de "listo" y el de "no encontramos el salón" se quedan angostos a
  propósito: son un mensaje de una frase, y una frase de 1240px de ancho no se
  lee, se recorre.
*/
#reserva {
  max-width: var(--k-columna);
  margin: 0 auto;
  padding: var(--k-space-8) var(--k-aire) var(--k-space-16);
}
#listo, #sinsalon { max-width: 640px; margin: 0 auto; padding: var(--k-space-5); }

/*
  En pantalla ancha, dos columnas.

  La columna de 640 px es lo correcto en un teléfono, que es donde se reserva
  casi siempre. En un escritorio de 1400 px deja el 55 % de la pantalla vacío a
  cada lado y obliga a rodar cuatro pantallas para lo que cabría en una: se ve
  angosto porque LO ES.

  El reparto no es simétrico: la izquierda lleva los servicios, que es la lista
  larga; la derecha lleva el día, la hora y los datos, que es la parte que
  cambia según lo que se elija a la izquierda. Así se ve el efecto de cada
  clic sin mover la vista.
*/
@media (min-width: 1000px) {
  #reserva {
    display: grid;
    /*
      `minmax(0, 1fr)` y no `1fr` a secas.

      `1fr` significa `minmax(auto, 1fr)`, y ese `auto` es el ancho MÍNIMO del
      contenido. La tira de catorce días no se puede encoger, así que se llevaba
      casi toda la fila y dejaba los servicios en una columna de 185 px, con los
      precios partidos en dos renglones. Con cero de mínimo, las dos mitades son
      mitades de verdad.
    */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0 var(--k-space-5);
    align-items: start;
  }
  /* La portada y el botón cruzan las dos columnas: son el principio y el final
     de la página, no de una de sus mitades. */
  #reserva > .portada,
  #reserva > .aviso,
  #reserva > #apartar,
  #reserva > p { grid-column: 1 / -1; }

  /* El paso 1 a la izquierda, ocupando lo que necesite; los otros tres
     apilados a la derecha. */
  #reserva > section:nth-of-type(1) { grid-column: 1; grid-row: 2 / span 3; }
  #reserva > section:nth-of-type(2) { grid-column: 2; grid-row: 2; }
  #reserva > section:nth-of-type(3) { grid-column: 2; grid-row: 3; }
  #reserva > section:nth-of-type(4) { grid-column: 2; grid-row: 4; }
}

/*
  Aquí la portada NO es la portada de la landing.

  Aquélla ocupa media pantalla a propósito: es lo primero que ve alguien que
  todavía no sabe si va a venir. En reservar y en el panel de la clienta es sólo
  una cabecera —ya sabe dónde está y a qué vino—, y con el `min-height` de la
  otra deja medio teléfono de fondo vacío antes del único dato que buscaba.
*/
#reserva .bloque, #reserva .portada,
#panel .bloque, #panel .portada, #entrar .bloque {
  max-width: none;
  margin: 0 0 var(--k-space-4);
  padding: var(--k-space-5) var(--k-space-6);
  min-height: 0;
  background: var(--k-surface);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
}
#reserva .portada, #panel .portada { display: block; }
/* Más pequeño que el de la portada —aquí ya se sabe en qué salón se está— pero
   con tamaño de título: con el de un párrafo, "Hola, Héctor" se leía como una
   nota al margen de su propia pantalla. */
#reserva h1, #panel h1 { font-size: clamp(1.6rem, 3vw, 2.1rem); line-height: 1.1; }
#reserva h2 { font-size: var(--k-h3); margin-bottom: var(--k-space-4); }
.marca { display: flex; align-items: center; gap: var(--k-space-4); }
.marca img, .marca .inicial {
  width: 60px; height: 60px;
  border-radius: var(--k-radius-xl);
  font-size: 1.6rem;
  object-fit: cover;
}

/*
  Los servicios, como casillas grandes.

  No un desplegable múltiple: una cita lleva dos o tres, y `select multiple`
  obliga a saber que hay que dejar una tecla puesta para elegir el segundo.
  Nadie lo sabe, y menos de pie en la calle.
*/
.elegibles { display: grid; gap: var(--k-space-2); }
.elegible {
  display: flex;
  align-items: center;
  gap: var(--k-space-3);
  min-height: 56px;
  padding: var(--k-space-2) var(--k-space-4);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
  cursor: pointer;
}
.elegible:has(input:checked) {
  border-color: var(--k-brand);
  background: color-mix(in srgb, var(--k-brand) 8%, var(--k-surface));
}
.elegible input { width: 20px; height: 20px; flex: none; accent-color: var(--k-brand); }
.elegible .detalle { display: block; color: var(--k-text-muted); font: var(--k-small) var(--k-font-sans); }

/*
  Los catorce días, en una tira que se arrastra.

  Un calendario entero pide entender un mes para elegir el martes; una tira
  contesta "¿mañana o pasado?", que es lo que de verdad se pregunta.
*/
.dias {
  display: flex;
  gap: var(--k-space-2);
  overflow-x: auto;
  padding-bottom: var(--k-space-1);
  scroll-snap-type: x proximity;
}
.dias button {
  flex: none;
  scroll-snap-align: start;
  display: grid;
  gap: 2px;
  justify-items: center;
  min-width: 58px;
  padding: var(--k-space-2) var(--k-space-1);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
  background: var(--k-surface);
  color: var(--k-text);
  font: inherit;
  cursor: pointer;
}
/* Lo que se pierde aquí no es decoración: es el día que la clienta acaba de
   elegir. Con el texto blanco sobre un color claro dejaba de ver su elección
   justo cuando va a confirmarla. */
.dias button[aria-pressed="true"] {
  background: var(--k-brand);
  border-color: var(--k-brand);
  color: var(--k-on-brand, #fff);
}
.dias .nombre, .dias .mes {
  font: var(--k-small) var(--k-font-sans);
  text-transform: uppercase;
  opacity: .8;
}
.dias .numero { font-size: 1.15rem; font-weight: 700; }

/* Las horas: pastillas anchas, que se tocan con el pulgar sin apuntar. */
/*
  "Con quién" y las horas son dos preguntas, y se tienen que ver como dos.

  El desplegable quedaba pegado a la primera fila de horas, sin más separación
  que la que hay entre dos horas cualquiera: se leía como si el nombre fuera
  parte de la lista. Elegir persona y elegir hora son decisiones distintas, y
  la segunda depende de la primera.
*/
#conquien-campo { margin-bottom: var(--k-space-5); }
#conquien-campo label {
  display: block;
  margin-bottom: var(--k-space-2);
  font: var(--k-small) var(--k-font-sans);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--k-text-muted);
}

/*
  Elegir con quién MIRANDO, cuando el negocio lo prefiere así.

  Un desplegable de nombres obliga a saberse la plantilla de memoria. La cara es
  como se elige de verdad a quien te va a cortar el pelo.

  Se desliza de lado en vez de partirse en varias filas: en un teléfono, cuatro
  personas en dos filas empujan las horas fuera de la pantalla, y las horas son
  el paso siguiente. Deslizar deja las dos cosas a la vista.
*/
.caras {
  display: flex;
  gap: var(--k-space-2);
  overflow-x: auto;
  padding-bottom: var(--k-space-1);
  scroll-snap-type: x proximity;
}
.cara {
  flex: none;
  scroll-snap-align: start;
  display: grid;
  justify-items: center;
  gap: 2px;
  width: 104px;
  padding: var(--k-space-3) var(--k-space-2);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
  background: var(--k-surface);
  color: var(--k-text);
  cursor: pointer;
}
.cara img, .cara-inicial {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  object-fit: cover;
  /* El mismo tamaño con foto y sin ella: un equipo a medio retratar se ve
     roto si las caras y las iniciales miden distinto. */
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--k-brand) 12%, var(--k-surface));
  font: 700 1.3rem/1 var(--k-font-sans);
  color: var(--k-brand-texto);
}
.cara-nombre {
  font: 600 .9rem/1.25 var(--k-font-sans);
  text-align: center;
  /*
    Dos renglones antes que recortar.

    Aquí sólo va el primer nombre, así que casi siempre cabe en uno. Pero
    "Guadalupe" o "Maximiliano" no, y cortarlos a "Guadalu…" es peor que
    bajarlos de línea: el nombre es lo único que distingue una cara de otra
    cuando no hay retrato. Las tarjetas se estiran todas por igual.
  */
  max-width: 100%;
  overflow-wrap: anywhere;
}
.cara-puesto {
  font: var(--k-small) var(--k-font-sans);
  color: var(--k-text-muted);
  text-align: center;
  max-width: 100%;
  overflow-wrap: anywhere;
  /*
    Dos renglones, y ahí se corta.

    "Especialista en uñas" en una línea sale como "Especialista …" y pierde
    justo la palabra que dice qué hace. En dos cabe entero. Pero el campo
    admite sesenta caracteres, y sin tope una frase larga estiraría su tarjeta
    al doble que las demás y la fila se vería rota.
  */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
.cara[aria-pressed="true"] {
  border-color: var(--k-brand);
  /* El color de marca de fondo, tenue: en sólido se comería la foto, que es
     justo lo que se está eligiendo. */
  background: color-mix(in srgb, var(--k-brand) 10%, var(--k-surface));
  box-shadow: inset 0 0 0 1px var(--k-brand);
}
/* "Con quien esté libre" no es una persona: su marca es el color del negocio,
   no una inicial que se confundiría con un nombre. */
.cara-cualquiera { color: var(--k-brand-texto); }

.horas { display: flex; flex-wrap: wrap; gap: var(--k-space-2); }
.horas button {
  min-height: var(--k-control);
  min-width: 88px;
  padding: 0 var(--k-space-4);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-pill);
  background: var(--k-surface);
  color: var(--k-text);
  font: 600 1rem/1 var(--k-font-sans);
  cursor: pointer;
}
.horas button[aria-pressed="true"] {
  background: var(--k-brand);
  border-color: var(--k-brand);
  color: var(--k-on-brand, #fff);
}

label {
  display: block;
  margin: var(--k-space-4) 0 var(--k-space-1);
  font: 600 var(--k-label) var(--k-font-sans);
}
.bloque input[type="text"],
.bloque input[type="tel"],
.bloque select {
  width: 100%;
  min-height: var(--k-control);
  padding: 0 var(--k-space-3);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-md);
  background: var(--k-surface);
  color: var(--k-text);
  font: inherit;
}

/* El aviso de que algo falta va en su color de sistema y con sitio propio:
   dentro de un campo se pierde, y arriba del todo obliga a rodar para leerlo. */
.aviso {
  margin: 0 0 var(--k-space-4);
  padding: var(--k-space-3) var(--k-space-4);
  border: 1px solid var(--k-danger);
  border-radius: var(--k-radius-md);
  background: color-mix(in srgb, var(--k-danger) 10%, var(--k-surface));
  color: var(--k-danger);
  font-weight: 600;
}

/* --- El panel de la clienta -------------------------------------------------
   Su próxima cita, su saldo y su historial. Se lee de pie, con una mano y con
   datos móviles: una columna, lo primero es lo más cercano en el tiempo, y
   nada que obligue a rodar para entender qué pasa hoy. */

/*
  Mi cuenta, con el ancho de la página y en dos columnas.

  Era una columna de 640px con todo apilado: la cita, los puntos, las fotos del
  probador y el historial, uno detrás de otro. En un teléfono está bien; en un
  escritorio son dos dedos de contenido en medio de la pantalla y hay que rodar
  cuatro veces para llegar a las visitas.

  A la izquierda lo que se CONSULTA de un vistazo —la próxima cita, los puntos,
  las visitas— y a la derecha las fotos, que es lo que ocupa sitio y lo que se
  mira con calma.
*/
#panel {
  max-width: var(--k-columna);
  margin: 0 auto;
  padding: var(--k-space-8) var(--k-aire) var(--k-space-16);
}
@media (min-width: 1000px) {
  #panel {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0 var(--k-space-5);
    align-items: start;
  }
  /* El encabezado y el pie cruzan: son el principio y el final de la página,
     no de una de sus mitades. */
  #panel > .cabeza-bloque,
  #panel > .firma,
  #panel > .acciones { grid-column: 1 / -1; }
  /* Las fotos del probador a la derecha, de arriba abajo: son lo que ocupa
     sitio y lo que se mira con calma. */
  #panel > #bloque-pruebas { grid-column: 2; grid-row: 2 / span 10; }
  /* Las salidas, abajo del todo y separadas de lo anterior: son el final de la
     pantalla, no una fila más de la columna izquierda. */
  .cuenta-salidas { margin-top: var(--k-space-8); }
}

#entrar, #sinsalon { max-width: 640px; margin: 0 auto; padding: var(--k-space-5); }
#panel h2 { font-size: var(--k-h3); margin-bottom: var(--k-space-4); }

/* La próxima cita, destacada de verdad: es el único dato por el que la mayoría
   abre esta página. */
.proxima {
  border: 1px solid var(--k-brand);
  border-radius: var(--k-radius-lg);
  background: color-mix(in srgb, var(--k-brand) 8%, var(--k-surface));
  padding: var(--k-space-5);
  margin-bottom: var(--k-space-4);
}
.proxima .cuando {
  margin: 0 0 var(--k-space-1);
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -.02em;
}
.proxima .que { margin: 0 0 var(--k-space-4); color: var(--k-text-muted); }
.proxima .acciones { margin: 0; }

/* El historial y las próximas de más abajo: filas tranquilas, sin competir con
   la de arriba. */
.cita-fila {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--k-space-4);
  padding: var(--k-space-3) 0;
  border-bottom: 1px solid var(--k-border);
}
.cita-fila:last-child { border-bottom: 0; }
.cita-fila .fecha { font-weight: 600; }
.cita-fila .detalle { color: var(--k-text-muted); font: var(--k-small) var(--k-font-sans); }

/* El saldo. Un número grande y solo: se mira una vez y se entiende. */
.saldos { display: flex; gap: var(--k-space-4); flex-wrap: wrap; margin-bottom: var(--k-space-5); }
.saldo {
  flex: 1 1 140px;
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
  background: var(--k-surface);
  padding: var(--k-space-4);
  text-align: center;
}
.saldo .cifra { display: block; font-size: 2rem; font-weight: 700; line-height: 1.1; }
.saldo .que { color: var(--k-text-muted); font: var(--k-small) var(--k-font-sans); }

/*
  Los premios se enseñan TODOS, y los que no alcanzan se ven apagados con su
  texto propio. Esconderlos deja al programa sin lo único que lo hace funcionar:
  saber a qué se está aspirando.

  Y el que alcanza no se distingue sólo por el color: lleva su etiqueta.
*/
.premios { display: grid; gap: var(--k-space-2); }
.premio {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--k-space-4);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-md);
  background: var(--k-surface);
  padding: var(--k-space-3) var(--k-space-4);
}
.premio.alcanza {
  border-color: var(--k-success);
  background: color-mix(in srgb, var(--k-success) 8%, var(--k-surface));
}
.premio .cuesta { color: var(--k-text-muted); font: var(--k-small) var(--k-font-sans); }
.premio .listo {
  color: var(--k-success);
  font: var(--k-small) var(--k-font-sans);
  font-weight: 700;
  white-space: nowrap;
}

/* Entrar: dos pasos y ninguno visible a la vez, porque el segundo no tiene
   sentido hasta que el primero mandó algo. */
#entrar { padding-top: var(--k-space-12); }
#entrar .bloque { max-width: 420px; margin: 0 auto var(--k-space-4); }
#entrar h2 { font-size: var(--k-h3); }
.codigo-campo {
  font-size: 1.6rem !important;
  letter-spacing: .35em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* --- La galería de trabajos --------------------------------------------------
   Una retícula que se acomoda sola: en el teléfono dos columnas, en el
   escritorio las que quepan. Cuadradas y recortadas al centro porque las fotos
   las sube el salón desde su teléfono y vienen de todas las formas: sin
   recortar, una vertical de tres mil píxeles rompe la fila entera. */

/*
  La galería es una TIRA que rueda, no una rejilla.

  En rejilla, cinco fotos daban cuatro arriba y una sola abajo: una huérfana
  que se lee como un error de maquetación. Y el número de fotos lo pone el
  negocio, así que no hay forma de elegir un número de columnas que le cuadre a
  todos.

  Rodando de lado, cualquier cantidad se ve bien y —lo que importa— se ve que
  HAY MÁS: una tira cortada por el borde invita a empujarla, que es justo lo
  que se quiere de una galería. Encima es el gesto natural en un teléfono.
*/
.galeria {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(150px, 1fr);
  gap: var(--k-space-3);
  overflow-x: auto;
  /* La barra que se lleva el espacio al aparecer hace saltar la página entera;
     reservarla desde el principio evita el brinco. */
  scrollbar-gutter: stable;
  padding-bottom: var(--k-space-3);
  /* Cada foto se planta entera al soltarla: media foto en el borde izquierdo
     es lo que hace que una tira parezca rota en vez de continua. */
  scroll-snap-type: x proximity;
}
.trabajo { margin: 0; scroll-snap-align: start; }

/*
  En pantalla ancha las fotos crecen. Todas IGUAL.

  Aquí la primera iba más ancha y apaisada, buscando ritmo editorial. Con las
  fotos de Héctor se vio por qué no: las sube desde el teléfono, en vertical, y
  estirar una vertical a 16:10 le corta la cabeza y los pies. Un ritmo que
  depende de que la primera foto sea apaisada no es un ritmo, es una apuesta —
  y la foto la elige el negocio, no nosotros.

  Todas del mismo tamaño y en vertical: es la proporción en la que se toma y en
  la que se mira un corte, un color o un peinado.
*/
@media (min-width: 720px) {
  .galeria { grid-auto-columns: 260px; }
}

/*
  El pie va DENTRO de la foto, y sólo cuando se mira.

  Debajo y en gris, el pie hacía que cada foto midiera distinto según lo largo
  que fuera su texto, y la tira quedaba dentada por abajo. Encima de la foto la
  altura es la misma para todas, y el texto aparece donde ya está el ojo.
*/
.trabajo {
  position: relative;
  overflow: hidden;
  border-radius: var(--k-radius-lg);
}
.trabajo img {
  display: block;
  width: 100%;
  /* En vertical: un corte, un peinado o un color se miran de arriba abajo, y
     el cuadrado le corta las puntas justo a lo que se está enseñando. */
  aspect-ratio: 3 / 4;
  object-fit: cover;
  background: var(--k-surface);
  transition: transform var(--k-slow, .35s) ease;
}
/* Al acercarse, la foto crece un poco DENTRO de su marco: dice que se puede
   abrir, sin mover nada de sitio. */
.trabajo:hover img,
.trabajo:focus-within img { transform: scale(1.04); }
@media (prefers-reduced-motion: reduce) {
  .trabajo img { transition: none; }
  .trabajo:hover img, .trabajo:focus-within img { transform: none; }
}

.trabajo figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--k-space-6) var(--k-space-4) var(--k-space-3);
  color: #fff;
  font: var(--k-small) var(--k-font-sans);
  font-weight: 600;
  /* El velo, para que el pie se lea sobre cualquier foto: la sube el negocio y
     no hay forma de saber si esa esquina es clara u oscura. */
  background: linear-gradient(transparent, rgba(13, 27, 32, .78));
}

/* --- Lo que dicen quienes ya vinieron ----------------------------------------
   La nota grande arriba y las opiniones debajo, en columnas que se acomodan
   solas. Las estrellas son texto: pesan cero, se ven igual en cualquier
   pantalla, y SIEMPRE van con el número al lado — quien no distingue bien las
   formas pequeñas lee "4.8", que dice lo mismo. */

.nota {
  display: flex;
  align-items: baseline;
  gap: var(--k-space-3);
  margin: calc(var(--k-space-5) * -1) 0 var(--k-space-6);
  font-size: 1.15rem;
}
.nota strong { font-size: 1.6rem; }
/*
  Las estrellas de una calificación.

  Llevaban `--k-warning`, que es el ámbar de "cuidado con esto": daba 3.4 de
  contraste sobre el fondo claro y, sobre todo, es el color con el que este
  sistema avisa de un problema. Una nota de 4.9 no es un aviso.

  El acento del negocio, en su variante legible: es el color de sus realces, y
  aquí realza exactamente lo que hay que mirar.
*/
.estrellas {
  color: var(--k-accent-texto);
  letter-spacing: .1em;
  white-space: nowrap;
}

.opiniones {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--k-space-4);
  /* Cada una mide lo suyo: un comentario de tres renglones al lado de uno de
     media línea no debe estirar la caja del segundo. */
  align-items: start;
}
.opinion {
  padding: var(--k-space-5);
  background: var(--k-surface);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
}
.opinion .cabeza {
  display: flex;
  align-items: center;
  gap: var(--k-space-3);
  margin: 0 0 var(--k-space-3);
}
.opinion .cabeza-texto {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

/*
  La cara de quien escribió: su inicial.

  Nadie sube un retrato para dejar una reseña, así que esto es lo que hay — y
  basta. Una lista de párrafos con un nombre encima se lee como un bloque de
  texto; lo que hace que una opinión pese es que se vea escrita por alguien.

  `--mezcla` lo pone la página a partir del propio nombre, para que dos
  opiniones seguidas no salgan idénticas. Pero siempre es el color del negocio
  mezclado con su fondo: un color inventado podría dejar la inicial ilegible,
  y aquí no hay forma de que eso pase, elijan la marca que elijan.
*/
.opinion-cara {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--k-brand) var(--mezcla, 14%), var(--k-surface));
  color: var(--k-brand-texto);
  font: 700 1.15rem/1 var(--k-font-sans);
}

/* El nombre deja de ser letra pequeña gris: es quien firma lo que se lee
   debajo, y lo firmado por un gris de pie de página no lo firma nadie. */
.opinion .quien {
  font: 600 .95rem/1.3 var(--k-font-sans);
  color: var(--k-text);
  overflow-wrap: anywhere;
}
.opinion .dice { margin: 0; }

/*
  La respuesta del salón: sangrada, como una cita dentro de otra.

  El filete es fino y del color del borde, no una franja gruesa en el color de
  la marca. Que sea otra voz ya lo dicen tres cosas —la sangría, el texto
  "Respuesta del salón:" y el gris—, y una barra de tres píxeles teñida encima
  de eso no informa de nada: sólo pinta.
*/
.opinion .contesta {
  margin: var(--k-space-4) 0 0;
  padding-left: var(--k-space-4);
  border-left: 1px solid var(--k-border);
  color: var(--k-text-muted);
  font: var(--k-small) var(--k-font-sans);
}

/* --- Calificar ---------------------------------------------------------------
   Botones de radio de verdad, no <span> con onclick: así funciona el teclado,
   funciona el lector de pantalla y cada estrella tiene su etiqueta. El radio se
   esconde a la vista pero SIGUE ahí para quien navega sin ver. */

.estrellas-elegir {
  display: flex;
  gap: var(--k-space-2);
  margin: 0 0 var(--k-space-5);
  padding: 0;
  border: 0;
}
.estrellas-elegir legend { padding: 0; margin-bottom: var(--k-space-2); }
.estrellas-elegir label {
  display: grid;
  place-items: center;
  width: 48px; height: 48px;
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-pill);
  background: var(--k-surface);
  font-weight: 700;
  cursor: pointer;
}
/* Fuera de la vista pero no del árbol: `display:none` lo sacaría también del
   teclado y del lector, que es justo lo contrario de lo que se busca. */
.estrellas-elegir input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}
.estrellas-elegir label:has(input:checked) {
  background: var(--k-brand);
  border-color: var(--k-brand);
  color: var(--k-on-brand, #fff);
}
/* El foco tiene que verse: si el radio está escondido, el anillo lo pinta la
   etiqueta o quien navega con teclado no sabe dónde está. */
.estrellas-elegir label:has(input:focus-visible) {
  outline: 2px solid var(--k-brand);
  outline-offset: 2px;
}

/* --- El mapa del salón, en su propia página ----------------------------------
   Se ve AQUÍ y no en otra pestaña: mandar a Google era sacar de la página a
   alguien que todavía no había reservado. Lo que quiere saber en este momento
   no es cómo llegar, es si le queda cerca — y para eso basta con verlo.

   Comparte `assets/mapa.js` con el selector del panel, en su modo de mirar: el
   pin va anclado a la coordenada del salón y viaja con el mapa, porque el local
   no se mueve cuando uno arrastra para ver qué hay alrededor. */

.mapa-publico {
  position: relative;
  height: clamp(220px, 38vw, 380px);
  margin: 0 0 var(--k-space-5);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
  overflow: hidden;
  background: var(--k-surface);
  /* Sin esto, arrastrar el mapa en un teléfono rueda la página debajo. */
  touch-action: none;
  cursor: grab;
  user-select: none;
}
.mapa-publico.agarrando { cursor: grabbing; }
.mapa-publico:focus-visible { outline: 2px solid var(--k-brand); outline-offset: 2px; }

.mapa-publico .mapa-lienzo { position: absolute; inset: 0; }
.mapa-publico .mapa-lienzo img {
  position: absolute;
  top: 0; left: 0;
  width: 256px; height: 256px;
  pointer-events: none;
}

/* El marcador se coloca desde el JavaScript con su propio `translate`, así que
   aquí NO se le pone uno: se pisarían. La punta la ajusta el segundo translate
   de esa misma regla. */
.mapa-publico .mapa-marca {
  position: absolute;
  top: 0; left: 0;
  width: 38px; height: 38px;
  color: var(--k-brand);
  pointer-events: none;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .35));
}
.mapa-publico .mapa-marca svg { width: 100%; height: 100%; }

.mapa-publico .mapa-zoom {
  position: absolute;
  top: var(--k-space-3); right: var(--k-space-3);
  display: grid;
  gap: 1px;
}
.mapa-publico .mapa-zoom button {
  width: 36px; height: 36px;
  padding: 0;
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-sm);
  background: var(--k-surface);
  color: var(--k-text);
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}

.mapa-publico .mapa-credito {
  position: absolute;
  bottom: 0; right: 0;
  margin: 0;
  padding: 2px var(--k-space-2);
  background: color-mix(in srgb, var(--k-surface) 85%, transparent);
  border-top-left-radius: var(--k-radius-sm);
  font: var(--k-small) var(--k-font-sans);
  color: var(--k-text-muted);
}
.mapa-publico .mapa-credito a { color: inherit; }

/* --- Lo que abre la foto en grande --------------------------------------------
   El visor en sí vive en `kabelo.css`: lo comparten la página del salón, Mi
   cuenta y la ficha del cliente en el panel. Aquí sólo se queda lo que lo
   abre, que es propio de esta página. */

/*
  Los botones que abren el visor no se ven como botones.

  Envuelven una foto que ya se veía bien: lo único que añaden es el cursor y el
  anillo de foco. Un borde o un fondo aquí convertirían la galería en una
  botonera.
*/
.trabajo > button,
.carta-lupa,
.persona-lupa {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
  border-radius: inherit;
}
/* El retrato ya no es una píldora: la esquina la pone la tarjeta, que recorta
   lo que se le sale. Sin encoger, o el texto de al lado se lo come. */
.persona-lupa { width: auto; flex: none; border-radius: 0; cursor: zoom-in; }
.trabajo > button:focus-visible,
.carta-lupa:focus-visible,
.persona-lupa:focus-visible {
  outline: 2px solid var(--k-brand);
  outline-offset: 3px;
}

/* --- La salida ---------------------------------------------------------------
   Un botón centrado y no un enlace al pie: es una salida, y un enlace subrayado
   del tamaño de la letra pequeña es difícil de acertar con el pulgar. */
.salida { display: flex; justify-content: center; margin: var(--k-space-8) 0 var(--k-space-4); }

/* --- Los datos que son ligas -------------------------------------------------
   El WhatsApp y el teléfono se tocan. Antes eran texto al lado de un icono:
   parecían un rótulo, y quien los tocaba no obtenía nada. */
.dato.es-liga {
  color: inherit;
  text-decoration: none;
  border-radius: var(--k-radius-md);
  padding: var(--k-space-1) var(--k-space-2);
  margin: calc(var(--k-space-1) * -1) calc(var(--k-space-2) * -1);
  transition: background var(--k-fast);
}
.dato.es-liga:hover { background: color-mix(in srgb, var(--k-brand) 10%, transparent); }
.dato.es-liga .v { text-decoration: underline; text-underline-offset: 3px; }

/* --- Que lo poco no se quede en una esquina ----------------------------------
   Las retículas usan `auto-fill`, que crea TODAS las pistas que caben aunque
   estén vacías, y las pistas tienen un ancho MÁXIMO: sin él, un salón con dos
   servicios los vería estirados a quinientos píxeles cada uno, que no son dos
   tarjetas sino dos carteles.

   Antes esto además CENTRABA lo que hubiera, para que dos tarjetas no
   quedaran contra el borde con media pantalla en blanco. Tenía sentido cuando
   los títulos también iban centrados; ahora que la página entera arranca del
   mismo margen izquierdo, centrar las tarjetas las dejaba veinticuatro píxeles
   a la derecha de su propio título.

   Así que arrancan a la izquierda, como todo lo demás. El hueco a la derecha
   de un catálogo de dos productos es el hueco que tiene un catálogo de dos
   productos: en una rejilla se lee como una lista corta, no como algo roto. */

@media (min-width: 900px) {
  .cartas { grid-template-columns: repeat(auto-fill, minmax(230px, 270px)); }
  .opiniones { grid-template-columns: repeat(auto-fill, minmax(280px, 340px)); }
}

/* Y los títulos de sección acompañan: un "Nuestros servicios" alineado a la
   izquierda sobre una fila de tarjetas centradas se lee como un error de
   maquetación. */
/*
  Los títulos de sección iban CENTRADOS, y ya no.

  Centrar funcionaba cuando cada bloque era un título y una lista debajo. Con
  la categoría encima y la salida a la derecha, centrar rompía las tres cosas:
  el ojal quedaba flotando, "ver todo" no tenía a qué alinearse, y el ojo
  perdía el borde izquierdo que le dice dónde empieza cada fila de tarjetas.

  La rejilla manda: todo arranca del mismo margen izquierdo, de arriba abajo.
  Es lo que separa una página compuesta de una lista de secciones.
*/

/* --- El probador, con la foto detrás -----------------------------------------
   Es lo que distingue a este producto de una página de citas cualquiera, y
   estaba anunciado con dos renglones sobre fondo liso. Con la portada del salón
   detrás parece lo que es: una invitación a probar algo.

   El velo NO es decoración, igual que en la portada: la foto la sube el negocio
   y no hay forma de saber si es clara. Sin él, un titular blanco sobre una
   pared blanca desaparece. */

/*
  Una banda, no un bloque más.

  Es lo que distingue a este producto de una página de citas cualquiera, y
  estaba anunciado con dos renglones centrados sobre un recuadro. Ahora tiene
  el peso de una banda: oscura, a lo ancho de la columna, con el texto a un
  lado y los motivos al otro.

  Oscura en el color del NEGOCIO y no en negro: el probador es una función de
  Kabelo, pero quien la usa lo hace desde la página de su salón.
*/
.probador {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(var(--k-space-6), 4vw, var(--k-space-12));
  align-items: center;
  margin: var(--k-space-10) var(--k-margen);
  padding: clamp(var(--k-space-6), 4vw, var(--k-space-12));
  border-radius: var(--k-radius-xl);
  /*
    Con `--k-brand-velo` y no con `--k-brand`.

    Todo el texto de esta banda va en claro, así que el fondo tiene que ser
    oscuro. Se mezclaba el color del negocio con un 12% de negro, que basta para
    oscurecer un azul petróleo y no para oscurecer un rosa: la banda de
    FMNavarro salía casi blanca con el titular encima, y "Ver cómo funciona"
    desaparecía del todo.

    `--k-brand-velo` es el mismo color del negocio bajado hasta donde hace falta
    —conserva el tono, sólo pierde luz— y lo calcula `assets/color.js`. Un color
    que ya venía oscuro no se toca, así que de fábrica esto no cambia.
  */
  background: linear-gradient(120deg,
    color-mix(in srgb, var(--k-brand-velo, var(--k-brand)) 88%, #000) 0%,
    color-mix(in srgb, var(--k-brand-velo, var(--k-brand)) 60%, #000) 100%);
  color: #f8f7f4;
}
@media (min-width: 860px) {
  /* El texto manda: es lo que convence. Los motivos acompañan. */
  .probador { grid-template-columns: 1.2fr 1fr; }
}
.probador h2 { color: #f8f7f4; }
/*
  El `.sub` del sistema es gris sobre claro, y sobre esto desaparece.

  Al 85% y no al 78%: sigue siendo claramente el texto secundario — esa es su
  razón de ser— pero siete puntos de opacidad son los que le faltaban para
  llegar a 4.5:1 sobre el velo. Era el renglón más flojo de la banda, y por eso
  es el que fija cuánto puede aclararse el velo. Ver `VELO_MAX` en color.js.
*/
.probador .sub { color: rgba(248, 247, 244, .85); }
/* Sobre el velo, y con su variante ajustada a él: el acento tal cual da 3.78:1
   incluso con los colores de fábrica, que alcanza para un icono pero no para
   texto. */
.probador .ojal { color: var(--k-accent-velo, var(--k-accent)); }
.probador-texto { display: flex; flex-direction: column; align-items: flex-start; }
.probador-texto .acciones { margin-top: var(--k-space-5); align-items: center; }
/* Sobre el fondo oscuro de la banda, el enlace en el color de marca desaparece:
   ese color ES el fondo. El acento es el que se ve. */
.probador .enlace { color: var(--k-accent-velo, var(--k-accent)); }
/* Sobre el fondo oscuro el botón claro es el que se ve: el primario del
   sistema es el color del negocio, que aquí ES el fondo. */
.probador .boton.principal {
  background: var(--k-accent);
  border-color: var(--k-accent);
  /* Sobre el ACENTO, que es otro color y puede pedir otra tinta. El respaldo es
     el gris oscuro de antes, que era el correcto para el dorado de fábrica. */
  color: var(--k-on-accent, #1a1a1a);
}

.probador-motivos {
  display: grid;
  gap: var(--k-space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}
.probador-motivos li {
  display: grid;
  gap: 2px;
  padding-left: var(--k-space-5);
  position: relative;
}
/* Una marca de verificación dibujada con dos bordes: son cero peticiones y
   cero bytes de icono para una decoración que se repite tres veces. */
.probador-motivos li::before {
  content: '';
  position: absolute;
  left: 0;
  top: .45em;
  width: 6px;
  height: 10px;
  border: solid var(--k-accent);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.probador-motivos strong { font-size: .98rem; }
.probador-motivos span { color: rgba(248, 247, 244, .72); font-size: .9rem; }

/*
  Con la foto del negocio detrás.

  El velo NO es decoración: la foto la sube el negocio y no hay forma de saber
  si es clara. Sin él, un titular blanco sobre una pared blanca desaparece — y
  aquí además hay que conservar el color de la marca, así que el degradado va
  encima de la foto y no en su lugar.
*/
/*
  Con la foto detrás, el mismo velo y una razón más para que sea oscuro.

  El extremo derecho abre hasta un 30% de transparencia para que se vea el
  local, y por ahí asoma la foto con sus propias zonas claras y oscuras. Sobre
  eso no se lee NINGÚN color de texto: aclarar la letra no habría arreglado
  nada. Lo que hace legible la banda es que el velo tape lo suficiente.
*/
.probador.con-foto {
  background:
    linear-gradient(120deg,
      color-mix(in srgb, var(--k-brand-velo, var(--k-brand)) 92%, #000) 0%,
      color-mix(in srgb, var(--k-brand-velo, var(--k-brand)) 70%, transparent) 100%),
    var(--fondo) center / cover no-repeat;
}

/* --- Preguntar antes de algo que no se deshace -------------------------------
   Un diálogo de la propia página y no `confirm()`. El del navegador sale con la
   tipografía del sistema, dice el dominio arriba y no se puede escribir: en un
   teléfono se ve igual que el aviso de una página que quiere mandar
   notificaciones, y eso es lo que se ha aprendido a cerrar sin leer. */

.preguntar {
  width: min(92vw, 400px);
  padding: var(--k-space-6);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
  background: var(--k-surface);
  color: var(--k-text);
  box-shadow: var(--k-shadow-lg);
}
.preguntar::backdrop { background: rgba(6, 12, 14, .55); }
.preguntar h3 { margin: 0 0 var(--k-space-2); }
.preguntar .sub { margin: 0; }
.preguntar .sub:empty { display: none; }

.preguntar-botones {
  display: flex;
  gap: var(--k-space-3);
  margin-top: var(--k-space-6);
}
/* Los dos ocupan la mitad: en un teléfono, un botón estrecho al lado de uno
   ancho se toca por error. */
.preguntar-botones .boton { flex: 1; }

/* --- Quien ya entró, reconocida ----------------------------------------------
   La entrada del menú decía "Mi cuenta" tanto para quien acababa de iniciar
   sesión como para quien nunca lo hizo: la clienta que YA está dentro no tenía
   forma de saberlo sin tocar y esperar. Con su nombre ahí, la página reconoce a
   quien vuelve — que es la mitad de la razón de tener cuenta. */

.con-sesion {
  display: inline-flex;
  align-items: center;
  gap: var(--k-space-2);
  padding: 6px var(--k-space-3);
  border-radius: var(--k-radius-pill);
  background: color-mix(in srgb, var(--marca, var(--k-brand)) 10%, transparent);
  font-weight: 600;
  /* Un nombre largo no puede empujar el resto del menú fuera de la barra. */
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* El punto de "estás dentro". Un pseudo-elemento y no un carácter en el texto:
   así no lo lee el lector de pantalla como parte del nombre. */
.con-sesion::before {
  content: '';
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--k-success);
}

/* --- "Ya sabemos quién eres" --------------------------------------------------
   Quien acaba de entrar a su cuenta no tiene que volver a escribir el nombre y
   el teléfono con los que entró. Es la clase de trámite que hace que alguien
   deje la reserva a medias y llame por teléfono. */

.soy-yo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--k-space-3);
  padding: var(--k-space-3) var(--k-space-4);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
  background: var(--k-surface);
}
.soy-yo strong { display: block; }
.soy-yo .sub { font-size: 13px; }
/* El botón no se encoge: "No soy yo" partido en dos renglones al lado de un
   nombre largo se lee como un error de maquetación. */
.soy-yo .boton { flex: none; }

/* --- Lo que se ha probado, en su cuenta --------------------------------------
   Es la razón por la que vuelve a abrir esta página. Con el antes al lado del
   después: sin el antes, el después es la foto de una persona rubia y no la
   prueba de un cambio. */

/*
  Las pruebas, en una línea de tiempo.

  Estaban una debajo de otra, con su pareja de fotos grandes y su fecha
  repetida en cada una. Tres pruebas del mismo día ya ocupaban dos pantallas;
  con cincuenta —que es una clienta de un año— la página se volvía interminable
  y el historial de visitas dejaba de existir para quien no rueda hasta el
  final.

  Aquí cada DÍA es un punto de la línea, y sus pruebas van a un lado en una
  tira. Con eso el alto deja de crecer con el número de pruebas y crece sólo
  con el número de días distintos, que es mucho menos.
*/
.linea-tiempo {
  list-style: none;
  margin: var(--k-space-4) 0 0;
  padding: 0;
  position: relative;
}

/* La línea: un borde en el contenedor de cada hito, no un pseudo-elemento
   suelto — así se corta sola en el último y no sobra un palo al aire. */
.hito {
  position: relative;
  padding: 0 0 var(--k-space-6) var(--k-space-6);
  border-left: 2px solid var(--k-border);
}
.hito:last-child { border-left-color: transparent; padding-bottom: 0; }

/* El punto del día, encima de la línea. */
.hito::before {
  content: '';
  position: absolute;
  left: -7px;
  top: 4px;
  width: 12px;
  height: 12px;
  border-radius: var(--k-radius-pill);
  background: var(--k-surface);
  border: 2px solid var(--k-border-control);
}
/* El día en el que se hizo algo de verdad se marca en el acento: de una lista
   de pruebas, ése es el que cuenta. */
.hito.con-elegida::before {
  background: var(--k-accent);
  border-color: var(--k-accent);
}

.hito-cuando {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--k-space-2);
  margin-bottom: var(--k-space-3);
}
.hito-cuando strong { font-size: 1rem; }
/* La fecha exacta al lado y en pequeño: lo que se busca es "hace dos semanas",
   pero quien quiera el día del mes lo tiene sin ir a otro sitio. */
.hito-cuando > span:not(.elegida-marca) {
  color: var(--k-text-muted);
  font: var(--k-small) var(--k-font-sans);
}

/*
  Las pruebas del día, en una tira que rueda.

  Alguien que se probó seis looks en una tarde no puede empujar seis pantallas
  hacia abajo a quien sólo quiere ver el día siguiente.
*/
.hito-pruebas {
  display: flex;
  gap: var(--k-space-3);
  overflow-x: auto;
  scrollbar-gutter: stable;
  padding-bottom: var(--k-space-2);
  scroll-snap-type: x proximity;
}
.hito-pruebas .prueba {
  flex: none;
  width: min(320px, 78vw);
  scroll-snap-align: start;
}

.pruebas {
  display: grid;
  gap: var(--k-space-4);
  margin-top: var(--k-space-3);
}
.prueba { margin: 0; }
.prueba .par {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--k-space-2);
}
/* Con una sola foto —las de antes de que se guardara el origen— ocupa el ancho
   entero en vez de dejar media rejilla vacía.

   Mira el BOTÓN y no la imagen: desde que cada foto abre en grande, el hijo
   directo del par es el botón que la envuelve, y `img:only-child` dejó de
   coincidir con nada. Una prueba sin "antes" volvía a salir en media columna. */
.prueba .par:has(> .prueba-lupa:only-child) { grid-template-columns: 1fr; }

.prueba img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: var(--k-radius-md);
  border: 1px solid var(--k-border);
  background: var(--k-bg);
}
.prueba figcaption {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--k-space-2);
  margin-top: var(--k-space-2);
  color: var(--k-text-muted);
  font: var(--k-small) var(--k-font-sans);
}

/* El que se hizo de verdad, marcado en la FOTO y no sólo con una etiqueta:
   quien mira esto está mirando caras, y una diferencia que vive en doce
   píxeles de texto no se ve al pasar. */
.prueba.elegida img { border-color: var(--k-success); border-width: 2px; }
.elegida-marca {
  padding: 2px var(--k-space-2);
  border-radius: var(--k-radius-pill);
  background: color-mix(in srgb, var(--k-success) 14%, transparent);
  color: var(--k-success);
  font-weight: 600;
}

/* --- Lo que ha dejado aquí ---------------------------------------------------
   Dentro del bloque de visitas: es el resumen de esas mismas visitas, y suelto
   en su propio bloque con dos cifras de dinero se lee como una factura. */

.gastado {
  display: flex;
  flex-wrap: wrap;
  gap: var(--k-space-4);
  margin-top: var(--k-space-4);
  padding-top: var(--k-space-4);
  border-top: 1px solid var(--k-border);
  color: var(--k-text-muted);
  font: var(--k-small) var(--k-font-sans);
}
.gastado strong {
  color: var(--k-text);
  font-size: 16px;
  font-variant-numeric: tabular-nums;
}

/* --- Premios y promociones en la página pública ------------------------------
   Razones para volver, dichas en números. Sólo si el negocio las enciende: una
   promoción que se anuncia se la pide todo el mundo, incluida la clienta que
   iba a pagar completo, y eso lo decide quien cobra. */

/*
  `auto-fill` y no `auto-fit`.

  Con `auto-fit` las columnas vacías se colapsan y dos promociones se repartían
  el ancho entero: dos tarjetas de seiscientos píxeles con una foto gigantesca,
  al lado de unas de servicio de trescientos. `auto-fill` conserva las
  columnas, así que dos promociones miden lo mismo que dos servicios.
*/
.premios-publicos, .promos-publicas {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--k-space-3);
  margin-top: var(--k-space-4);
}

.premio-publico, .promo-publica {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--k-space-3);
  padding: var(--k-space-4);
  border: 1px solid var(--k-border);
  border-radius: var(--k-radius-lg);
  background: var(--k-surface);
}

/* El coste en el acento y con cifras tabulares: es el número que se compara
   entre premios, y comparar es todo lo que se hace en esta lista. */
.premio-publico span {
  flex: none;
  color: var(--k-accent-texto);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* La promoción se lee de arriba abajo: el nombre engancha y la descripción
   explica, que es distinto del premio, donde lo que importa es el número. */
.promo-publica {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--k-space-1);
}
.promo-publica span { color: var(--k-text-muted); }

/*
  La promoción con foto: imagen a sangre arriba, texto con su margen debajo.

  Es la misma decisión que en la carta, y por la misma razón: el relleno de la
  tarjeta pasa a los HIJOS en vez de estar en la tarjeta, o la imagen queda con
  un marco blanco a los lados — el "está al ras y se ve feo" de la captura.

  Y hay que excluir `.carta-lupa` además de `.carta-foto`: la foto va envuelta
  en un botón para poder abrirse, así que el hijo directo es el botón. Ya me
  costó una vez en la carta.
*/
.promo-publica.con-foto {
  padding: 0;
  overflow: hidden;
  gap: 0;
}
.promo-publica.con-foto > *:not(.carta-foto):not(.carta-lupa) {
  padding-left: var(--k-space-4);
  padding-right: var(--k-space-4);
}
.promo-publica.con-foto > strong {
  padding-top: var(--k-space-4);
  padding-bottom: var(--k-space-1);
}
.promo-publica.con-foto > span { padding-bottom: var(--k-space-4); }

/*
  Y cuando sólo ALGUNAS llevan foto, la que no lleva reserva su hueco.

  Sin esto la fila queda dentada: una tarjeta de texto mide lo que mide su
  texto, y al lado de una con foto se ve como media tarjeta.

  El relleno del hueco es LITERALMENTE el de la carta —una sola regla sirve a
  las dos—, con su tijera y su degradado. Un gris plano al lado de una tarjeta
  que sí tiene la tijera se leería como una imagen que no cargó.
*/
.promos-publicas:has(.con-foto) .promo-publica:not(.con-foto) {
  padding: 0;
  overflow: hidden;
  gap: 0;
  /*
    Las dos cosas que hacen falta porque esta tarjeta es un flex en columna, y
    la de la carta no.

    `stretch` porque el hueco se dibuja con `aspect-ratio` y sin ancho: con
    `flex-start` heredado mide cero y desaparece — la tarjeta guardaba el alto
    y no enseñaba nada. Y `flex-start` en el eje principal porque el
    `space-between` heredado repartía hueco, nombre y descripción por toda la
    tarjeta, dejando el texto flotando en medio.
  */
  align-items: stretch;
  justify-content: flex-start;
}
.promos-publicas:has(.con-foto) .promo-publica:not(.con-foto) > strong {
  padding: var(--k-space-4) var(--k-space-4) var(--k-space-1);
}
.promos-publicas:has(.con-foto) .promo-publica:not(.con-foto) > span {
  padding: 0 var(--k-space-4) var(--k-space-4);
}

/* --- Opinar de un toque -----------------------------------------------------
   La pantalla que abre el enlace del WhatsApp. Existe para que dejar una
   opinión cueste un toque: quien tiene que leer, decidir y rellenar, cierra la
   pestaña. Toda la pantalla cabe sin rodar en un teléfono. */

.opinar {
  display: grid;
  place-items: center;
  min-height: 100dvh;
  padding: var(--k-space-6) var(--k-space-4);
}
.opinar-caja {
  width: 100%;
  max-width: 460px;
  text-align: center;
}
.opinar-logo {
  max-height: 56px;
  max-width: 180px;
  margin: 0 auto var(--k-space-4);
  object-fit: contain;
}
.opinar h1 { margin: var(--k-space-1) 0 var(--k-space-2); }
.opinar #detalle { margin: 0 0 var(--k-space-6); }

/*
  Las cinco estrellas: la interfaz entera.

  Grandes y separadas porque se tocan con el pulgar y sin apuntar — en un
  teléfono, dos estrellas juntas son una estrella equivocada y una opinión que
  no dice lo que quien la dejó quería decir.

  Se apagan en gris y se encienden en el color de la marca. Todas las de la
  izquierda se encienden con la elegida, que es como se lee una calificación:
  cuatro estrellas son cuatro encendidas, no la cuarta sola.
*/
.estrellas-elegir {
  display: flex;
  justify-content: center;
  gap: var(--k-space-2);
  margin-bottom: var(--k-space-3);
}
.estrellas-elegir button {
  flex: none;
  width: 56px;
  height: 56px;
  padding: 0;
  border: 0;
  border-radius: var(--k-radius-md);
  background: transparent;
  color: var(--k-border-control);
  font-size: 2.4rem;
  line-height: 1;
  cursor: pointer;
  /* La transición es corta a propósito: esto tiene que sentirse instantáneo,
     porque lo es — la opinión ya se guardó cuando termina la animación. */
  transition: color .12s ease, transform .12s ease;
}
.estrellas-elegir button:hover { transform: scale(1.12); }
.estrellas-elegir button[aria-pressed="true"] { color: var(--k-accent); }

.opinar #comentario-caja { margin-top: var(--k-space-6); text-align: left; }
.opinar textarea {
  width: 100%;
  margin-bottom: var(--k-space-3);
  padding: var(--k-space-3);
  border: 1px solid var(--k-border-control);
  border-radius: var(--k-radius-md);
  background: var(--k-surface);
  color: var(--k-text);
  font: 1rem/1.5 var(--k-font-sans);
  resize: vertical;
}

/* El aviso de que ya quedó, en verde: es lo contrario de un error, y con el
   estilo de `.aviso` a secas saldría rojo diciendo "gracias". */
.opinar .aviso.ok {
  border-color: #2E7D52;
  background: color-mix(in srgb, #2E7D52 12%, var(--k-surface));
  color: #1B5E3A;
}
[data-theme="dark"] .opinar .aviso.ok { color: #8FE3B4; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .opinar .aviso.ok { color: #8FE3B4; }
}

/*
  La invitación al probador, dentro de Mi cuenta.

  En horizontal —texto a la izquierda, botón a la derecha— y no como una tarjeta
  más: es una acción, no información. Con el mismo peso que los bloques de al
  lado se leería como otro apartado que hay que leer, y lo que se busca es que
  se toque.

  El color del negocio, tenue: destaca sobre la columna sin gritar más que la
  próxima cita, que es lo que de verdad importa en esta pantalla.
*/
.probador-invita {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--k-space-4);
  flex-wrap: wrap;
  background: color-mix(in srgb, var(--k-brand) 8%, var(--k-surface));
  border-color: color-mix(in srgb, var(--k-brand) 22%, var(--k-border));
}
.probador-invita h2 { margin: 0 0 var(--k-space-1); }
.probador-invita .sub { margin: 0; max-width: 46ch; }

/* --- Los textos legales -----------------------------------------------------
   Una columna estrecha y con aire: esto se lee de corrido, y un párrafo que
   cruza mil doscientos píxeles se pierde de renglón en renglón. */

.legal {
  max-width: 68ch;
  margin: 0 auto;
  padding: var(--k-space-10) var(--k-aire) var(--k-space-16);
}
.legal h1 { margin: var(--k-space-1) 0 var(--k-space-2); }
.legal section { margin-top: var(--k-space-10); }
.legal h2 {
  padding-bottom: var(--k-space-2);
  border-bottom: 1px solid var(--k-border);
}
.legal h3 {
  margin: var(--k-space-6) 0 var(--k-space-2);
  font: 600 1.05rem/1.35 var(--k-font-sans);
}
.legal p, .legal li { line-height: 1.65; }
.legal ul { margin: 0 0 var(--k-space-4); padding-left: var(--k-space-5); }
.legal li { margin-bottom: var(--k-space-2); }
.legal-nota {
  margin-top: var(--k-space-10);
  padding-top: var(--k-space-4);
  border-top: 1px solid var(--k-border);
}

/*
  El aviso del negocio, dentro de su página.

  Va en un desplegable y no en una página aparte: quien lo busca lo busca AHÍ
  —en el pie de la página del salón— y mandarlo a otra dirección es perderlo. Y
  quien no lo busca no debería tropezarse con tres pantallas de texto legal.
*/
.aviso-negocio {
  margin-top: var(--k-space-6);
  padding-top: var(--k-space-4);
  border-top: 1px solid var(--k-border);
}
.aviso-negocio summary {
  cursor: pointer;
  font: 600 .9rem/1.4 var(--k-font-sans);
  color: var(--k-text-muted);
}
.aviso-negocio summary:hover { color: var(--k-text); }
.aviso-negocio .texto {
  margin-top: var(--k-space-3);
  max-width: 68ch;
  /* Los saltos de línea que escribió el negocio se respetan: lo escribió como
     párrafos y sin esto llegaría como un solo bloque. */
  white-space: pre-wrap;
  line-height: 1.65;
  color: var(--k-text-muted);
}
