/* Escaparate público (tienda.html).

Estaba embebido en un <style> dentro del HTML: 771 líneas, más CSS que
panel.css entero, fuera del sistema de estilos y sin acceso cómodo a los
tokens de tema. Para reutilizar una clase había que ir a copiarla de dentro
de un HTML — pasó de verdad al añadir los botones de contacto. */

/* Header (inyectado por layout.js) fijo arriba SOLO en el escaparate:
       queda pegado al hacer scroll. La altura real se mide por JS y se
       publica en --sf-header-h para que el buscador se pegue justo debajo. */
    #site-header {
      position: sticky;
      top: 0;
      z-index: 1030;
    }

    /* ===================================================================
       ESCAPARATE PÚBLICO (tienda.html) — estilo "catálogo denso" tipo
       Uber/Localeo, con los tokens de lixai (rojo de acento, DM Serif
       Display para titulares). Los tokens viven en :root (no en .sf)
       porque los modales (modalFiltro, modalTienda, modalProducto) son
       hermanos de .sf en el DOM, no descendientes — si los tokens
       colgaran de .sf, esos modales no los heredarían y sus var(--x)
       quedarían sin resolver (chips y botones sin fondo/borde). No hay
       colisión de nombres: theme-tokens.css no se carga en esta página.
       Todo el resto de reglas sigue scoped a .sf, sin tocar
       header/footer (que inyecta layout.js) ni el resto del sitio. */
    :root {
      --tx: #1a1a1a;
      --tx2: #555452;
      --tx3: #928f8a;
      --brd: #e8e7e5;
      --brd-md: #dcdbd8;
      --surf: #ffffff;
      --bg: #f7f6f4;
      --red: #e8364e;
      --red-lt: #fde8e4;
      --red-mid: #fadcd6;
      --r: 10px;
      --r-lg: 14px;
      --r-xl: 18px;
      --r-pill: 999px;
      --sh-xs: 0 1px 2px rgba(0, 0, 0, 0.05);
      --sh-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
      --sh-md: 0 6px 20px rgba(0, 0, 0, 0.08);
      --disp: "DM Serif Display", Georgia, serif;
    }
    .sf {
      background: var(--bg);
    }

    /* — Banner —
       Altura fija en vw, no en px: el banner es a sangre (sin .container,
       ocupa todo el ancho de la ventana). Con una altura fija de verdad
       (240px) un monitor ancho de escritorio forma una caja rectangular
       MUY panorámica (p.ej. 1900x240 = ratio ~8:1); background-size:cover
       tiene que ampliar la foto mucho más de lo que amplía en móvil para
       taparla entera, así que se ve recortada/con zoom raro -- no es la
       posición (cover_position_y) la que falla, es el ratio de la caja.
       En vw la altura crece con el ancho de ventana, así que ese ratio se
       mantiene razonable en cualquier tamaño (clamp evita que se dispare
       en monitores ultra-anchos o se quede corta en tablet). */
    .sf-banner {
      height: clamp(220px, 20vw, 420px);
      position: relative;
      /* Fondo por defecto (tienda sin portada): patrón sutil de comercio,
         velado con una capa crema al 62% para que quede discreto, no recargado. */
      background: linear-gradient(rgba(247, 246, 244, 0.62), rgba(247, 246, 244, 0.62)),
        url("../img/fondo-tienda.png");
      background-size: auto, 260px;
      background-repeat: repeat;
      border-bottom: 1px solid var(--brd);
    }
    /* Con portada real (JS añade .has-cover): foto a cover + degradado oscuro. */
    .sf-banner.has-cover { border-bottom: 0; }
    .sf-banner.has-cover::after {
      content: "";
      position: absolute;
      inset: 0;
      background: linear-gradient(to bottom, rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.42));
    }
    @media (max-width: 575px) {
      .sf-banner { height: 150px; }
    }

    /* — Barra de info de tienda — */
    .sf-infobar {
      background: var(--surf);
      border-bottom: 1px solid var(--brd);
    }
    .sf-infobar-inner {
      display: flex;
      align-items: flex-start;
      gap: 20px;
      padding: 22px 0;
      flex-wrap: wrap;
    }
    .sf-avatar {
      width: 78px;
      height: 78px;
      border-radius: var(--r-lg);
      border: 3px solid var(--surf);
      box-shadow: var(--sh-md);
      object-fit: cover;
      /* Fondo crema sutil: si el logo es un PNG transparente (o falta y se usa
         el placeholder), el avatar nunca se ve vacío/roto. */
      background: #f5efe3;
      /* Sube sobre el banner (por encima de la imagen de fondo). */
      margin-top: -56px;
      position: relative;
      z-index: 2;
      flex-shrink: 0;
    }
    @media (max-width: 575px) {
      .sf-avatar { width: 58px; height: 58px; margin-top: -42px; }
      /* En vez de envolver en varias filas (se veía cortado en pantallas
         estrechas), scroll lateral de una sola fila — mismo patrón que
         .sf-cats. */
      .sf-actions {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
        width: 100%;
        padding-bottom: 2px;
      }
      .sf-actions::-webkit-scrollbar { display: none; }
      .sf-btn-action { flex-shrink: 0; }
    }
    .sf-info-main { flex: 1 1 260px; min-width: 0; }
    .sf-name-row {
      display: flex;
      align-items: center;
      gap: 10px;
      flex-wrap: wrap;
      margin-bottom: 4px;
    }
    .sf-name {
      font-family: var(--disp);
      font-size: clamp(1.35rem, 3vw, 2rem);
      font-weight: 400;
      color: var(--tx);
      letter-spacing: -0.02em;
      line-height: 1.1;
      margin: 0;
    }
    .sf-meta {
      display: flex;
      align-items: center;
      gap: 14px;
      flex-wrap: wrap;
      margin-top: 6px;
    }
    .sf-meta-pill {
      display: inline-flex;
      align-items: center;
      gap: 5px;
      font-size: 0.76rem;
      font-weight: 500;
      color: var(--tx3);
    }
    .sf-meta-pill a { color: inherit; text-decoration: none; }
    .sf-badge {
      font-size: 0.6rem;
      font-weight: 700;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      padding: 4px 10px;
      border-radius: var(--r-pill);
    }
    .sf-badge.open { background: var(--good-soft, #e6f9f0); color: #157347; border: 1px solid #c2e8d5; }
    .sf-badge.closed { background: var(--red-lt); color: #b02a37; border: 1px solid var(--red-mid); }

    .sf-actions { display: flex; gap: 8px; flex-wrap: wrap; flex-shrink: 0; }
    .sf-btn-action {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      background: var(--surf);
      border: 1.5px solid var(--brd-md);
      border-radius: var(--r-pill);
      padding: 7px 15px;
      font-size: 0.78rem;
      font-weight: 600;
      color: var(--tx2);
      cursor: pointer;
      white-space: nowrap;
      transition: border-color 0.16s, color 0.16s, box-shadow 0.16s;
    }
    .sf-btn-action:hover { border-color: var(--red); color: var(--red); box-shadow: var(--sh-xs); }
    .sf-cart-badge {
      background: var(--red);
      color: #fff;
      border-radius: var(--r-pill);
      font-size: 0.68rem;
      font-weight: 700;
      padding: 1px 6px;
      line-height: 1.4;
    }

    /* — Insignias de disponibilidad (agotado / bajo pedido) — */
    .sf-avail-badge {
      display: inline-flex;
      align-items: center;
      width: fit-content;
      padding: 3px 10px;
      border-radius: var(--r-pill);
      font-size: 0.72rem;
      font-weight: 600;
    }
    .sf-avail-badge.backorder { background: #fef3c7; color: #92400e; }
    .sf-avail-badge.out { background: var(--red-lt); color: var(--red); }
    .sf-tile-avail {
      position: absolute;
      top: 8px;
      left: 8px;
      padding: 3px 9px;
      border-radius: var(--r-pill);
      font-size: 0.68rem;
      font-weight: 700;
      z-index: 2;
    }
    .sf-tile-avail.backorder { background: #fef3c7; color: #92400e; }
    .sf-tile-avail.out { background: var(--red-lt); color: var(--red); }
    /* — Insignia "en oferta" sobre la imagen — lado opuesto a
       sf-tile-avail para que no choquen si un producto está a la vez de
       oferta y bajo pedido/agotado. — */
    .sf-tile-sale {
      position: absolute;
      top: 8px;
      right: 8px;
      padding: 3px 9px;
      border-radius: var(--r-pill);
      font-size: 0.68rem;
      font-weight: 700;
      z-index: 2;
      background: var(--red);
      color: #fff;
    }
    .sf-mp-sale {
      position: absolute;
      top: 12px;
      left: 12px;
      padding: 4px 11px;
      border-radius: var(--r-pill);
      font-size: 0.74rem;
      font-weight: 700;
      z-index: 2;
      background: var(--red);
      color: #fff;
    }
    .crt-line-tag {
      display: inline-flex;
      margin-top: 4px;
      margin-right: 4px;
      padding: 2px 8px;
      border-radius: var(--r-pill);
      background: #fef3c7;
      font-size: 0.68rem;
      font-weight: 600;
      color: #92400e;
    }
    .crt-line-tag.sale { background: var(--red-lt); color: var(--red); }

    /* — Selector de cantidad (modal producto + líneas del carrito) — */
    .sf-qty-stepper { display: flex; align-items: center; gap: 6px; border: 1.5px solid var(--brd-md); border-radius: var(--r-pill); padding: 3px 6px; flex-shrink: 0; }
    .sf-qty-btn { width: 26px; height: 26px; border-radius: 50%; border: none; background: var(--bg); color: var(--tx); font-size: 1rem; font-weight: 700; cursor: pointer; display: flex; align-items: center; justify-content: center; line-height: 1; }
    .sf-qty-btn:hover { background: var(--red-lt); color: var(--red); }
    .sf-qty-stepper span { min-width: 18px; text-align: center; font-weight: 600; font-size: 0.85rem; }

    /* — Carrito (paso a paso, dentro de #modalCarrito) — */
    .crt-step { display: none; }
    .crt-step.on { display: block; }
    .crt-line { padding: 10px 0; }
    .crt-line:not(:last-child) { border-bottom: 1px solid var(--brd); }
    .crt-line-img { width: 44px; height: 44px; border-radius: 10px; object-fit: cover; background: var(--bg); border: 1px solid var(--brd); flex-shrink: 0; }
    .crt-line-name { font-weight: 600; font-size: 0.85rem; color: var(--tx); }
    .crt-line-price { font-size: 0.78rem; color: var(--tx3); margin-top: 2px; }
    .crt-line-price .sf-price-original { font-family: inherit; font-size: inherit; margin-right: 6px; }

    /* — Buscador sticky — */
    .sf-searchbar {
      background: var(--surf);
      border-bottom: 1px solid var(--brd);
      position: sticky;
      top: var(--sf-header-h, 56px);
      z-index: 200;
      padding: 12px 0;
    }
    .sf-search-wrap { position: relative; max-width: 640px; margin: 0 auto; }
    .sf-search-icon {
      position: absolute;
      left: 15px;
      top: 50%;
      transform: translateY(-50%);
      font-size: 0.9rem;
      color: var(--tx3);
      pointer-events: none;
    }
    .sf-search-input {
      width: 100%;
      border: 1.5px solid var(--brd-md);
      border-radius: var(--r-pill);
      padding: 10px 18px 10px 42px;
      font-size: 0.9rem;
      color: var(--tx);
      background: var(--bg);
      outline: none;
      transition: border-color 0.16s, box-shadow 0.16s, background 0.16s;
    }
    .sf-search-input:focus {
      border-color: var(--red);
      box-shadow: 0 0 0 3px rgba(232, 54, 78, 0.1);
      background: var(--surf);
    }

    /* — Fila buscador + botón filtro — */
    .sf-search-row { display: flex; align-items: center; gap: 10px; max-width: 640px; margin: 0 auto; }
    .sf-search-row .sf-search-wrap { flex: 1; margin: 0; }
    .sf-btn-filter {
      position: relative;
      display: inline-flex;
      align-items: center;
      gap: 6px;
      flex-shrink: 0;
      background: var(--surf);
      border: 1.5px solid var(--brd-md);
      border-radius: var(--r-pill);
      padding: 9px 16px;
      font-size: 0.82rem;
      font-weight: 600;
      color: var(--tx2);
      cursor: pointer;
      white-space: nowrap;
      transition: border-color 0.16s, color 0.16s;
    }
    .sf-btn-filter:hover { border-color: var(--tx3); }
    .sf-btn-filter .dot {
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: var(--red);
      display: none;
    }
    .sf-btn-filter.has-filters .dot { display: inline-block; }

    /* — Pastillas de filtros aplicados (buscador) — */
    .sf-active-filters { display: flex; flex-wrap: wrap; gap: 6px; max-width: 640px; margin: 10px auto 0; }
    .sf-filter-pill {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      background: var(--red-lt);
      border: 1px solid var(--red-mid);
      border-radius: var(--r-pill);
      padding: 5px 10px 5px 13px;
      font-size: 0.74rem;
      font-weight: 600;
      color: var(--red);
      cursor: pointer;
    }
    .sf-filter-pill .x { font-size: 0.72rem; opacity: 0.85; }
    .sf-filter-pill:hover { background: var(--red-mid); }

    /* — Grupos dentro del modal de filtro (precio + marca) — */
    .sf-filter-group { margin-bottom: 16px; }
    .sf-filter-group:last-child { margin-bottom: 4px; }
    .sf-filter-label {
      font-size: 0.7rem;
      font-weight: 700;
      color: var(--tx3);
      text-transform: uppercase;
      letter-spacing: 0.04em;
      margin-bottom: 7px;
    }
    .sf-filter-chips { display: flex; flex-wrap: wrap; gap: 8px; }
    /* Un catálogo grande puede traer cientos de marcas distintas: scroll
       propio para que no se salgan del modal. */
    #sfBrandChips { max-height: 240px; overflow-y: auto; padding-right: 4px; padding-bottom: 2px; }

    /* — Chips de categoría — */
    .sf-catbar { background: var(--surf); border-bottom: 1px solid var(--brd); padding: 12px 0; }

    /* La barra de secciones de la CARTA sí se queda pegada bajo la cabecera,
       a diferencia de los chips de Productos y Servicios. La razón es el
       tamaño: en Productos los chips filtran y se usan una vez; aquí saltan,
       y en una carta de 224 platos se salta muchas veces. Sin sticky, después
       del primer salto hay que volver arriba del todo para dar el segundo.
       z-index por debajo del buscador (200) porque nunca coinciden: son de
       vistas distintas. */
    #sfMenuCatBar {
      position: sticky;
      top: var(--sf-header-h, 56px);
      z-index: 199;
    }
    .sf-cats {
      display: flex;
      gap: 8px;
      overflow-x: auto;
      scrollbar-width: none;
      -webkit-overflow-scrolling: touch;
      padding-bottom: 2px;
    }
    .sf-cats::-webkit-scrollbar { display: none; }
    .sf-chip {
      display: inline-flex;
      align-items: center;
      gap: 5px;
      background: var(--surf);
      border: 1.5px solid var(--brd);
      border-radius: var(--r-pill);
      padding: 6px 14px;
      font-size: 0.76rem;
      font-weight: 600;
      color: var(--tx2);
      cursor: pointer;
      white-space: nowrap;
      transition: all 0.14s;
    }
    .sf-chip:hover { border-color: var(--tx3); }
    .sf-chip.active { background: var(--tx); border-color: var(--tx); color: #fff; font-weight: 700; }
    .sf-subcats { margin-top: 8px; }
    .sf-subcats .sf-chip { padding: 4px 12px; font-size: 0.7rem; font-weight: 500; }
    /* — Sección productos — */
    .sf-main { padding: 26px 0 48px; }
    .sf-section-head { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; }
    .sf-section-title {
      font-family: var(--disp);
      font-size: 1.2rem;
      font-weight: 400;
      color: var(--tx);
      margin: 0;
      letter-spacing: -0.01em;
    }
    .sf-count { font-size: 0.78rem; color: var(--tx3); font-weight: 500; }

    /* Alto mínimo: al recargar/filtrar, sfGrid se vacía un instante antes de
       rellenarse con la nueva página — sin esto la página se encoge, el
       footer sube a la vista y vuelve a bajar de golpe al llegar los
       productos (salto visual). */
    #sfGrid { min-height: 60vh; }
    @media (max-width: 575px) {
      #sfGrid { min-height: 40vh; }
    }

    /* — Tarjeta de producto — */
    .sf-tile {
      background: var(--surf);
      border: 1px solid var(--brd);
      border-radius: var(--r-xl);
      overflow: hidden;
      cursor: pointer;
      transition: box-shadow 0.2s, transform 0.2s;
      display: flex;
      flex-direction: column;
      height: 100%;
    }
    .sf-tile:hover { box-shadow: var(--sh-md); transform: translateY(-3px); }
    .sf-tile-img {
      position: relative;
      aspect-ratio: 1;
      background: var(--bg);
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
    }
    .sf-tile-img img {
      width: 100%;
      height: 100%;
      object-fit: contain;
      padding: 12px;
      transition: transform 0.3s ease;
    }
    .sf-tile:hover .sf-tile-img img { transform: scale(1.05); }
    .sf-tile-body { padding: 11px 13px 13px; flex: 1; display: flex; flex-direction: column; gap: 5px; }
    .sf-tile-name {
      font-size: 0.83rem;
      font-weight: 600;
      color: var(--tx);
      line-height: 1.35;
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      overflow: hidden;
      flex: 1;
    }
    .sf-tile-brand { font-size: 0.68rem; color: var(--tx3); font-weight: 500; text-transform: uppercase; letter-spacing: 0.03em; }
    .sf-tile-price {
      font-family: var(--disp);
      font-size: 1.05rem;
      font-weight: 400;
      color: var(--tx);
      letter-spacing: -0.02em;
    }
    .sf-tile-price.on-sale { color: var(--red); }
    .sf-price-original {
      font-family: var(--disp);
      font-size: 0.85rem;
      color: var(--tx3);
      text-decoration: line-through;
      margin-right: 4px;
    }
    .sf-svc-meta .sf-price-original { font-family: inherit; font-size: inherit; }
    /* El precio final de algo rebajado, EN LA MISMA LÍNEA que el tachado (la
       fila de un servicio, la línea del carrito). Antes heredaba el gris de
       .sf-svc-meta: el tachado y el nuevo precio salían del mismo
       color y del mismo tamaño, así que la rebaja no se veía — que es
       exactamente lo contrario de para lo que se pone una rebaja. */
    .sf-precio-oferta { color: var(--red); font-weight: 700; }

    /* "8 € socios" al lado del precio normal. Deliberadamente NO rojo y NO
       tachado: el rojo y el tachado son el idioma de una rebaja ("costaba más
       y hoy cuesta menos"), y aquí los dos precios son ciertos a la vez —
       depende de quién pregunte, no de cuándo. Se pinta como un dato más,
       discreto, que es como lo publican las asociaciones en su web. */
    .sf-precio-socio {
      display: inline-block;
      margin-left: 8px;
      padding: 1px 8px;
      border-radius: var(--r-pill);
      background: var(--bg-soft, #f0f1f3);
      color: var(--text-muted, #6b7280);
      font-size: 0.72rem;
      font-weight: 600;
      white-space: nowrap;
      vertical-align: 1px;
    }

    /* El "-X%" donde no hay imagen sobre la que poner .sf-tile-sale: la fila
       de un servicio en modo lista. Mismo rojo y mismo peso que la insignia
       de la tarjeta, para que sea el mismo idioma en las dos vistas. */
    .sf-desc-inline {
      display: inline-block;
      margin-left: 6px;
      padding: 1px 7px;
      border-radius: var(--r-pill);
      background: var(--red);
      color: #fff;
      font-size: 0.68rem;
      font-weight: 700;
      vertical-align: 1px;
    }

    /* El chip "Ofertas" de la fila de categorías. Rojo relleno desde el
       principio, no gris como los demás: es el único que no es una sección
       del catálogo sino un reclamo, y con el icono naranja de etiqueta sobre
       fondo blanco pasaba desapercibido entre quince categorías. Lleva el
       descuento mayor de la tienda dentro, porque "Ofertas" a secas no
       distingue un 5% de un 40% y ese número es lo que decide si se pulsa. */
    .sf-chip.sf-chip-oferta {
      background: var(--red);
      border-color: var(--red);
      color: #fff;
    }
    .sf-chip.sf-chip-oferta:hover { background: var(--red); border-color: var(--red); color: #fff; }
    /* Activo: se invierte (blanco con borde rojo) en vez de quedarse igual.
       Con el resto de chips, "activo" es oscurecerse; aquí ya está lleno, así
       que la única forma de que se note el cambio es al revés. */
    .sf-chip.sf-chip-oferta.active {
      background: #fff;
      border-color: var(--red);
      color: var(--red);
    }

    /* — Skeleton — */
    .sf-skel {
      background: linear-gradient(90deg, var(--bg) 25%, var(--brd) 50%, var(--bg) 75%);
      background-size: 200% 100%;
      animation: sf-shine 1.4s infinite;
      border-radius: var(--r);
    }
    @keyframes sf-shine {
      0% { background-position: 200% 0; }
      100% { background-position: -200% 0; }
    }

    /* — Botón ver más — */
    .sf-btn-more {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      background: var(--surf);
      border: 1.5px solid var(--brd-md);
      border-radius: var(--r-pill);
      padding: 11px 26px;
      font-size: 0.88rem;
      font-weight: 600;
      color: var(--tx2);
      cursor: pointer;
      transition: border-color 0.16s, color 0.16s, box-shadow 0.16s;
    }
    .sf-btn-more:hover { border-color: var(--tx); color: var(--tx); box-shadow: var(--sh-sm); }
    .sf-btn-more:disabled { opacity: 0.6; cursor: default; }

    /* — Empty state — */
    .sf-empty { text-align: center; padding: 56px 20px; }
    .sf-empty-icon { font-size: 2.6rem; display: block; margin-bottom: 14px; opacity: 0.5; }
    .sf-empty p { font-size: 0.9rem; color: var(--tx3); }

    /* — Modal tienda: tabs — */
    .sf-modal-tabs { display: flex; border-bottom: 1px solid var(--brd); }
    .sf-modal-tab {
      flex: 1;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      padding: 12px 8px;
      font-size: 0.82rem;
      font-weight: 600;
      color: var(--tx3);
      border: none;
      background: none;
      border-bottom: 2px solid transparent;
      cursor: pointer;
      margin-bottom: -1px;
      transition: color 0.15s, border-color 0.15s;
    }
    .sf-modal-tab:hover { color: var(--tx2); }
    .sf-modal-tab.active { color: var(--tx); border-bottom-color: var(--tx); }
    .sf-panel { display: none; }
    .sf-panel.active { display: block; }

    /* — Horario — */
    .sf-hora-row {
      display: flex;
      justify-content: space-between;
      padding: 11px 20px;
      border-bottom: 1px solid var(--brd);
      font-size: 0.85rem;
    }
    .sf-hora-row:last-child { border-bottom: none; }
    .sf-hora-row .dia { font-weight: 600; color: var(--tx); }
    .sf-hora-row .horas { color: var(--tx3); font-weight: 500; }
    .sf-hora-row.hoy { background: #f0fdf4; }
    .sf-hora-row.hoy .dia, .sf-hora-row.hoy .horas { color: #157347; font-weight: 700; }
    .sf-estado-badge {
      display: inline-flex;
      align-items: center;
      gap: 7px;
      font-size: 0.8rem;
      font-weight: 700;
      padding: 6px 14px;
      border-radius: var(--r-pill);
      margin: 16px 20px 6px;
      /* Neutro por defecto: horario sin indicar no es lo mismo que cerrado. */
      background: var(--surface-2, #f1f1f1);
      color: var(--tx3, #6b7280);
    }
    .sf-estado-badge.open { background: #e6f9f0; color: #157347; }
    .sf-estado-badge.closed { background: var(--red-lt); color: #b02a37; }
    .sf-estado-badge .dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; background: var(--tx4, #9ca3af); }
    .sf-estado-badge.open .dot { background: #16a34a; }
    .sf-estado-badge.closed .dot { background: var(--red); }
    /* Aviso de excepción de hoy (festivo u horario puntual): va debajo del
       badge, en su propia línea, porque contradice al cuadro semanal de
       abajo y hay que verlo antes de leerlo. Ámbar, no rojo: no es un
       cierre, es un "hoy es distinto". */
    .sf-horario-especial {
      margin: 0 20px 6px;
      padding: 7px 12px;
      border-radius: var(--r-md, 8px);
      background: #fff8e1;
      color: #8a6100;
      font-size: 0.78rem;
      font-weight: 600;
      line-height: 1.4;
    }

    /* — Info cards — */
    .sf-info-card { display: flex; align-items: flex-start; gap: 13px; background: var(--bg); border-radius: var(--r-lg); padding: 14px 16px; }
    .sf-info-card .ic { font-size: 1.4rem; flex-shrink: 0; line-height: 1; margin-top: 2px; }
    .sf-info-card h6 { font-size: 0.84rem; font-weight: 700; color: var(--tx); margin-bottom: 4px; }
    .sf-info-card p { font-size: 0.78rem; color: var(--tx3); line-height: 1.6; margin: 0; }
    .sf-info-card a { color: var(--red); text-decoration: none; font-weight: 600; }
    .sf-social-btn {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      background: var(--surf);
      border: 1.5px solid var(--brd-md);
      border-radius: var(--r-pill);
      padding: 7px 15px;
      font-size: 0.78rem;
      font-weight: 600;
      color: var(--tx2);
      text-decoration: none;
      transition: border-color 0.15s, color 0.15s;
    }
    .sf-social-btn:hover { border-color: var(--tx); color: var(--tx); }

    /* — Mapa — */
    .sf-btn-maps {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      flex: 1;
      background: var(--tx);
      color: #fff;
      border: none;
      border-radius: var(--r-pill);
      padding: 13px 20px;
      font-size: 0.9rem;
      font-weight: 700;
      cursor: pointer;
      text-decoration: none;
      transition: background 0.18s;
    }
    .sf-btn-maps:hover { background: var(--red); color: #fff; }
    .sf-btn-maps.ghost { background: none; border: 1.5px solid var(--brd-md); color: var(--tx2); }
    .sf-btn-maps.ghost:hover { border-color: var(--tx); color: var(--tx); }
    /* Botón "Aceptar" del modal de filtro: azul, para distinguirlo de
       "Quitar filtros" (mismo molde que .sf-btn-maps, distinto color). */
    .sf-btn-maps.accept { background: #2563eb; }
    .sf-btn-maps.accept:hover { background: #1d4ed8; }
    .sf-addr-card { background: var(--bg); border: 1px solid var(--brd); border-radius: var(--r-lg); padding: 12px 16px; display: flex; gap: 10px; }
    .sf-addr-card .nombre { font-size: 0.84rem; font-weight: 700; color: var(--tx); }
    .sf-addr-card .dir { font-size: 0.77rem; color: var(--tx3); margin-top: 2px; line-height: 1.5; }

    /* — Modal producto — */
    /* OJO: nunca combinar con la clase h-100 (height:100%). aspect-ratio:1
       calcula el ancho a partir del alto cuando el alto ya es definido — con
       h-100, el alto lo fija el flex del row (row.g-0 estira ambas columnas
       a la más alta, ver .col-md-7 al lado), así que si el texto se hace
       largo esta caja se ensancha con él y se sale de su columna,
       tapando el texto. Sin h-100, el alto se calcula al revés (a partir
       del ancho de su propia columna), que es lo que se quiere. */
    .sf-mp-img {
      position: relative;
      aspect-ratio: 1;
      background: var(--bg);
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
    }
    .sf-mp-img img { max-height: 300px; width: auto; object-fit: contain; padding: 18px; }
    .sf-mp-name { font-family: var(--disp); font-size: 1.4rem; font-weight: 400; color: var(--tx); letter-spacing: -0.02em; line-height: 1.15; }
    .sf-mp-price { font-family: var(--disp); font-size: 1.7rem; font-weight: 400; color: var(--tx); letter-spacing: -0.03em; }
    .sf-mp-price.on-sale { color: var(--red); }
    .sf-ficha-row { display: flex; justify-content: space-between; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--brd); font-size: 0.82rem; }
    .sf-ficha-row:last-child { border-bottom: none; }
    .sf-ficha-row .k { color: var(--tx3); font-weight: 500; }
    .sf-ficha-row .v { color: var(--tx); font-weight: 600; text-align: right; }

    /* — Pestañas Productos / Servicios (solo si el comercio tiene ambos) — */
    .sf-catalog-tabs { background: var(--surf); border-bottom: 1px solid var(--brd); }
    .sf-catalog-tabs .container { display: flex; gap: 4px; }
    .sf-catalog-tab {
      padding: 13px 18px; font-size: 0.85rem; font-weight: 600; color: var(--tx3);
      background: none; border: none; border-bottom: 2px solid transparent; cursor: pointer;
      margin-bottom: -1px; transition: color 0.15s, border-color 0.15s;
    }
    .sf-catalog-tab:hover { color: var(--tx2); }
    .sf-catalog-tab.active { color: var(--tx); border-bottom-color: var(--red); }

    /* — Servicios: los pasos de la reserva, dentro de #modalReserva — */
    /* Se cayó .sf-booking-card (un max-width de 560px centrado): el asistente
       era una columna dentro de la página y necesitaba no irse a lo ancho de
       una pantalla de ordenador. Ahora vive en un modal, que ya trae su
       propio ancho. */
    #sfServiciosSection { padding: 0 0 48px; }
    .sf-booking-option {
      display: block; width: 100%; text-align: left; padding: 13px 16px; margin-bottom: 8px;
      border: 1.5px solid var(--brd-md); border-radius: var(--r-lg); background: var(--surf); cursor: pointer;
      transition: border-color 0.15s;
    }
    .sf-booking-option:hover { border-color: var(--red); }
    .sf-booking-option .opt-sub { display: block; font-weight: 400; font-size: 0.8rem; color: var(--tx3); margin-top: 2px; }
    .sf-slot-grid { display: flex; flex-wrap: wrap; gap: 8px; }
    .sf-slot-btn {
      min-width: 74px; padding: 8px 14px; border-radius: var(--r-pill); border: 1.5px solid var(--brd-md);
      background: var(--surf); font-size: 0.82rem; font-weight: 600; color: var(--tx2); cursor: pointer;
    }
    .sf-slot-btn:hover { border-color: var(--red); color: var(--red); }
    .sf-slot-btn:disabled {
      color: var(--tx3); text-decoration: line-through; cursor: not-allowed; background: var(--bg);
    }
    .sf-booking-step { display: none; }
    .sf-booking-step.on { display: block; }
    .sf-booking-back { font-size: 0.85rem; color: var(--tx3); background: none; border: none; padding: 0 0 12px; cursor: pointer; }
    .sf-booking-back:hover { color: var(--tx); }

    .sf-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
    .sf-cal-dow { text-align: center; font-size: 0.68rem; color: var(--tx3); font-weight: 600; padding: 2px 0; }
    .sf-cal-day {
      aspect-ratio: 1; border: 1.5px solid var(--brd-md); border-radius: 8px; background: var(--surf);
      display: flex; align-items: center; justify-content: center; font-size: 0.82rem; cursor: pointer;
    }
    .sf-cal-day.is-empty { visibility: hidden; cursor: default; }
    .sf-cal-day.st-available { border-color: #22c55e; color: #16a34a; font-weight: 700; }
    .sf-cal-day.st-full { border-color: var(--red-mid); color: #c99; text-decoration: line-through; cursor: not-allowed; }
    .sf-cal-day.st-closed, .sf-cal-day.st-past { color: var(--tx3); cursor: not-allowed; }
    .sf-cal-day.st-past { visibility: hidden; }
    .sf-cal-day.is-selected { background: var(--tx); color: #fff !important; border-color: var(--tx); }
    /* — Reservar mesa (modal): las tres preguntas en una pantalla — */
    .rv-pregunta { font-size: 0.82rem; font-weight: 700; color: var(--tx); margin: 14px 0 8px; }
    .rv-pregunta:first-child { margin-top: 0; }
    /* El día y la hora elegidos, arriba del formulario: al pasar al paso 2 el
       selector se esconde y sin esto nada dice para cuándo se reserva. */
    .rv-resumen {
      background: var(--bg); border-radius: var(--r); padding: 10px 12px; margin-bottom: 12px;
      font-size: 0.82rem; font-weight: 600; color: var(--tx);
    }
    .rv-turno { font-size: 0.76rem; color: var(--tx3); margin: 12px 0 6px; }
    .rv-turno:first-child { margin-top: 0; }
    .rv-sin-horas { font-size: 0.82rem; color: var(--tx3); margin: 0; }

    .sf-cal-legend { display: flex; gap: 12px; font-size: 0.74rem; color: var(--tx3); margin-top: 8px; flex-wrap: wrap; }
    .sf-cal-legend span { display: inline-flex; align-items: center; gap: 4px; }
    .sf-cal-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
    /* Gris y no rojo desde que la tarjeta tiene botón de "Reservar": eran dos
       llamadas a la acción apiladas, y la de abajo gritaba más que la de
       arriba. Mismo papel (y mismo gris) que .sf-svc-more en la fila. */
    .sf-tile-more { font-size: 0.68rem; color: var(--tx3); font-weight: 600; margin-top: 2px; }

    /* ——— Servicios: la fila del catálogo ———
       Clases propias (.sf-svc-*) y no las de la carta. Hasta ahora la fila de
       un servicio y la de un plato compartían cascarón (.sf-service-list-row),
       que además de tener nombre de servicio lo pintaba tienda-carta.js: poner
       aquí el botón de "Reservar" habría movido la carta de todos los
       restaurantes sin que nadie lo pidiera. Servicios es una app aparte, con
       su interruptor y su pantalla propia — también en el CSS.

       Qué cambia respecto a la fila de antes (nombre, duración, precio y una
       flechita "›"): aquella era un índice. Para saber qué es "drenaje
       linfático" o "punción seca" había que abrir el detalle y volver atrás si
       no era eso — diez servicios, diez idas y venidas. Ahora la fila dice qué
       es y deja reservarlo; el detalle sigue ahí, pero deja de ser peaje. */
    /* Contenedor a secas: dentro van los bloques de categoría, y las columnas
       las pone .sf-menu-grid (la misma rejilla de la carta). Antes era una
       columna de 640px centrada — se leía bien con cinco servicios, pero un
       centro de estética con treinta dejaba media pantalla en blanco a la
       derecha y obligaba a recorrerlos todos para encontrar uno. */
    .sf-svc-list { display: block; }
    /* La fila se estira a la altura de su vecina (la rejilla ya lo hace) y el
       cuerpo con ella, para que los botones de las dos columnas queden a la
       misma altura aunque una descripción ocupe dos líneas y la otra ninguna.
       La foto no se estira: se queda arriba, en su cuadrado. */
    .sf-svc-row {
      display: flex; align-items: stretch; gap: 14px; padding: 12px 14px; cursor: pointer;
      background: var(--surf); border: 1px solid var(--brd); border-radius: var(--r-lg);
      transition: border-color 0.15s, box-shadow 0.15s;
    }
    .sf-svc-row:hover { border-color: var(--red); box-shadow: var(--sh-xs); }
    .sf-svc-img {
      width: 64px; height: 64px; border-radius: var(--r); object-fit: cover;
      flex-shrink: 0; align-self: flex-start; background: var(--bg);
    }
    .sf-svc-body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
    .sf-svc-name { font-weight: 600; font-size: 0.9rem; color: var(--tx); }
    .sf-svc-meta { font-size: 0.78rem; color: var(--tx3); margin-top: 2px; }
    /* Dos líneas y corta. Tres ya empujan el botón del siguiente servicio
       fuera de la pantalla del móvil, que es justo lo que se quiere evitar. */
    .sf-svc-desc {
      font-size: 0.79rem; color: var(--tx2); line-height: 1.5; margin-top: 6px;
      display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    }
    .sf-svc-acciones { display: flex; align-items: center; gap: 10px; margin-top: auto; padding-top: 10px; }
    /* Negro y no rojo: es el mismo botón que el "Reservar" del detalle
       (.sf-btn-maps). El rojo del escaparate está gastado en ofertas y
       descuentos — una columna de botones rojos se lee como una columna de
       rebajas. */
    .sf-svc-cta {
      background: var(--tx); color: #fff; border: none; border-radius: var(--r-pill);
      padding: 8px 18px; font-size: 0.79rem; font-weight: 700; cursor: pointer;
      transition: background 0.15s; flex-shrink: 0;
    }
    .sf-svc-cta:hover { background: var(--red); color: #fff; }
    /* Empujado a la derecha: la acción manda y esto es la salida secundaria.
       Sustituye a la flecha "›" que había al final de la fila — decía que
       había algo detrás, pero no qué. */
    .sf-svc-more { font-size: 0.75rem; color: var(--tx3); font-weight: 600; margin-left: auto; white-space: nowrap; }
    /* En la tarjeta el botón va a lo ancho: la columna es estrecha y un pill
       centrado a media anchura se queda flotando. */
    .sf-svc-cta-tile { display: block; width: 100%; margin-top: 8px; padding: 7px 12px; font-size: 0.75rem; }
    .sf-tile .sf-tile-more { text-align: center; }

    /* — Carta: los platos de una sección — */
    /* En el móvil, un plato por fila (la fila entera ya va justa: foto,
       nombre, alérgenos y precio). En pantalla de ordenador, dos: con una
       sola columna, una carta de 224 platos son metros de scroll con media
       pantalla en blanco a la derecha. El corte en 768px es el mismo que usa
       la rejilla de la Galería. */
    .sf-menu-grid { display: grid; grid-template-columns: 1fr; gap: 8px; }
    @media (min-width: 768px) { .sf-menu-grid { grid-template-columns: 1fr 1fr; } }

    /* — Alojamiento: pase de fotos y calendario de ocupación — */
    .sf-mp-miniaturas { display: flex; gap: 6px; padding: 8px; overflow-x: auto; }
    .sf-mp-mini {
      width: 62px; height: 48px; object-fit: cover; border-radius: 8px;
      cursor: pointer; flex-shrink: 0; opacity: 0.6;
      border: 2px solid transparent; transition: opacity 0.12s, border-color 0.12s;
    }
    .sf-mp-mini.es-activa, .sf-mp-mini:hover { opacity: 1; border-color: var(--red); }

    .sf-cal-aloj { margin-top: 14px; border-top: 1px solid var(--brd); padding-top: 12px; }
    .sf-cal-aloj-tit { font-weight: 700; font-size: 0.86rem; margin-bottom: 8px; }
    .sf-cal-meses { display: flex; gap: 18px; flex-wrap: wrap; }
    .sf-cal-mes-tit {
      font-size: 0.74rem; font-weight: 600; text-transform: capitalize;
      margin-bottom: 4px; color: var(--tx);
    }
    .sf-cal-rejilla {
      display: grid; grid-template-columns: repeat(7, 22px); gap: 2px;
      font-size: 0.66rem; text-align: center;
    }
    .sf-cal-cab { color: var(--tx3); font-weight: 600; }
    .sf-cal-dia { border-radius: 5px; padding: 3px 0; }
    /* Libre en verde suave y ocupado tachado en rojo: el color solo no basta
       —hay quien no lo distingue— así que la noche cogida además va tachada. */
    .sf-cal-dia.es-libre { background: #e8f6ec; color: #17632f; }
    .sf-cal-dia.es-ocupado { background: #fdeaea; color: #a32020; text-decoration: line-through; }
    .sf-cal-dia.es-pasado { color: var(--tx3); opacity: 0.45; }
    .sf-cal-leyenda-aloj {
      display: flex; gap: 14px; margin-top: 8px; font-size: 0.7rem; color: var(--tx3);
    }
    .sf-cal-leyenda-aloj span { display: inline-flex; align-items: center; gap: 5px; }
    .sf-cal-muestra { width: 12px; height: 12px; border-radius: 4px; display: inline-block; }
    .sf-cal-muestra.es-libre { background: #e8f6ec; border: 1px solid #b9e0c6; }
    .sf-cal-muestra.es-ocupado { background: #fdeaea; border: 1px solid #f0bcbc; }
    .sf-cal-aviso { font-size: 0.7rem; color: var(--tx3); margin: 8px 0 0; }
    /* La barra del calendario con sus dos flechas: el escaparate trae medio
       año de disponibilidad de una vez, así que pasar de mes no vuelve a
       pedir nada — se repinta con lo que ya está en memoria. */
    .sf-cal-barra {
      display: flex; align-items: center; justify-content: space-between;
      gap: 10px; margin-bottom: 8px;
    }
    .sf-cal-barra .sf-cal-aloj-tit { margin: 0; }
    .sf-cal-paso {
      border: 1px solid var(--brd-md); background: var(--surf); color: var(--tx);
      border-radius: var(--r); padding: 3px 10px; font-size: 0.85rem; cursor: pointer;
    }
    .sf-cal-paso:hover { border-color: var(--tx); }
    .sf-cal-vacio { visibility: hidden; }

    /* — El plato — */
    /* El texto manda y la foto acompaña: nombre, descripción, precios y
       alérgenos en columna a la izquierda, y la foto a la derecha, grande.
       Antes era al revés (foto de 64 px a la izquierda, precio suelto a la
       derecha) y pasaban dos cosas: la foto no se veía —un plato es
       precisamente lo que hay que ver— y el precio quedaba lejos de su
       etiqueta cuando había variantes. */
    /* El cascarón de la fila (caja blanca, borde, radio) vivía en
       .sf-service-list-row, compartida con el catálogo de servicios. Se trajo
       aquí tal cual al separar las dos apps: la carta y los servicios se
       parecen, pero no son la misma pantalla y ya no comparten clase.
       (cursor:pointer y el borde rojo al pasar por encima vienen de esa
       herencia; un plato no se pulsa, así que sobran — pero eso es un cambio
       visual de la carta y no toca hacerlo de refilón aquí.) */
    .sf-plato {
      display: flex; align-items: flex-start; gap: 12px; padding: 12px 14px; cursor: pointer;
      background: var(--surf); border: 1px solid var(--brd); border-radius: var(--r-lg);
      transition: border-color 0.15s, box-shadow 0.15s;
    }
    .sf-plato:hover { border-color: var(--red); box-shadow: var(--sh-xs); }
    .sf-plato-txt { flex: 1; min-width: 0; }
    .sf-plato-desc {
      font-size: 0.78rem; line-height: 1.45; color: var(--tx3); margin-top: 3px;
    }
    .sf-plato-img {
      width: 116px; height: 92px; object-fit: cover; border-radius: var(--r);
      flex-shrink: 0; background: var(--bg);
    }
    @media (min-width: 992px) { .sf-plato-img { width: 132px; height: 104px; } }

    /* Los precios, en columnas: etiqueta arriba en gris pequeño, importe
       debajo. Es lo que hace legible un plato con cuatro variantes. */
    .sf-plato-precios { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 8px; }
    .sf-plato-op { display: flex; flex-direction: column; line-height: 1.2; }
    .sf-plato-op-et { font-size: 0.68rem; color: var(--tx3); }
    .sf-plato-op strong { font-size: 0.92rem; white-space: nowrap; }

    /* — Alérgenos con icono — */
    .sf-plato-alergenos { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }
    .sf-alergeno {
      display: inline-flex; align-items: center; justify-content: center;
      width: 21px; height: 21px; border-radius: 50%; flex-shrink: 0;
    }
    .sf-alergeno svg { width: 13px; height: 13px; display: block; }
    /* Un alérgeno sin dibujo (uno nuevo que la ley añada mañana) se enseña
       escrito, que es mejor que no enseñarlo. */
    .sf-alergeno--texto {
      width: auto; padding: 1px 7px; border-radius: 999px; font-size: 0.66rem;
      background: var(--bg); color: var(--tx3);
    }
    /* La leyenda del final: qué significa cada icono de los que salen en ESTA
       carta. En el móvil no hay ratón que enseñe el `title`. */
    .sf-alergenos-leyenda {
      display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
      margin: 26px auto 0; max-width: 640px; padding: 12px 14px;
      background: var(--surf); border: 1px solid var(--brd); border-radius: var(--r-lg);
    }
    .sf-alergenos-leyenda-tit { font-size: 0.74rem; font-weight: 700; color: var(--tx); }
    .sf-alergeno-item {
      display: inline-flex; align-items: center; gap: 5px;
      font-size: 0.72rem; color: var(--tx3);
    }

    /* Cabecera de sección con letra pequeña debajo ("Pizzas rojas" + "base de
       tomate y fior di latte"). .sf-section-head es flex en una fila, que es
       lo que quiere una cabecera sin descripción; con ella hacen falta dos
       filas, así que aquí se apila y la fila de dentro se queda con el flex. */
    /* La cabecera de sección de la CARTA es más grande y en negrita que la
       del resto del escaparate: una carta se lee de arriba abajo y de un tirón
       —224 platos en 24 secciones— así que el título tiene que cortar la
       lectura y dejar claro que empieza otra cosa. En Productos no hace falta:
       allí las categorías se filtran con los chips y casi nunca hay más de
       tres bloques seguidos en pantalla. */
    .sf-menu-head { display: block; margin-top: 34px; }
    .sf-menu-head:first-child { margin-top: 0; }
    .sf-menu-head-fila { display: flex; align-items: baseline; gap: 12px; }
    /* En sans y no en la serif de titulares como el resto del escaparate: DM
       Serif Display no tiene negrita de verdad, así que pedirle 700 la fabrica
       el navegador engordando los trazos, y a este tamaño se ve emborronada.
       Comparadas las dos en pantalla, la sans en 800 corta mucho mejor —que es
       justo lo que se le pide a esta cabecera. */
    .sf-menu-head .sf-section-title {
      font-family: "DM Sans", -apple-system, "Segoe UI", Arial, sans-serif;
      font-size: 1.5rem;
      font-weight: 800;
      letter-spacing: -0.02em;
    }
    @media (max-width: 575px) {
      .sf-menu-head .sf-section-title { font-size: 1.35rem; }
      .sf-menu-head { margin-top: 28px; }
    }
    .sf-menu-head-desc {
      margin: 4px 0 0;
      font-size: 0.82rem;
      color: var(--tx2);
      max-width: 70ch;
    }

    /* Selector de idioma, dentro de la barra pegada y a la izquierda de los
       chips. `flex-shrink: 0` porque comparte fila con una tira de secciones
       que se desplaza sola: sin eso, en el móvil lo aplastan hasta la bandera
       y media. La flecha va dibujada en el fondo (un `select` nativo la pinta
       distinta en cada sistema y a veces encima del texto). */
    /* Solo la barra de la CARTA se pone en fila: la de Productos lleva dentro
       dos filas apiladas (categorías y subcategorías) y en flex se pondrían
       una al lado de la otra. */
    #sfMenuCatBar .container { display: flex; align-items: center; gap: 10px; }
    #sfMenuCatBar .sf-cats { flex: 1; min-width: 0; }
    .sf-idioma-btn {
      flex-shrink: 0;
      display: inline-flex;
      align-items: center;
      gap: 6px;
      max-width: 160px;
      background: var(--surf);
      border: 1.5px solid var(--brd-md);
      border-radius: var(--r-pill);
      padding: 5px 11px;
      font-size: 0.76rem;
      font-weight: 600;
      color: var(--tx);
      cursor: pointer;
    }
    .sf-idioma-btn:hover { border-color: var(--tx3); }
    .sf-idioma-btn > span:not(.sf-idioma-caret) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .sf-idioma-caret { color: var(--tx3); font-size: 0.7rem; }

    /* Banderas dibujadas (js/banderas.js): el borde finito es lo que evita que
       la bandera de Japón o la de Polonia, que son casi todo blanco, se
       deshagan sobre el fondo blanco de la pastilla. */
    .bandera { border-radius: 2px; border: 1px solid rgba(0, 0, 0, 0.14); flex-shrink: 0; display: block; }
    .bandera-codigo {
      display: inline-block;
      background: var(--bg);
      border-radius: 3px;
      padding: 1px 4px;
      font-weight: 700;
      color: var(--tx2);
    }

    .sf-hoja-opcion {
      display: flex;
      align-items: center;
      gap: 10px;
      width: 100%;
      background: var(--surf);
      border: 1.5px solid var(--brd);
      border-radius: var(--r);
      padding: 12px 14px;
      margin-bottom: 8px;
      font-size: 0.9rem;
      font-weight: 600;
      color: var(--tx);
      cursor: pointer;
    }
    .sf-hoja-opcion.activa { border-color: var(--tx); }

    /* Buscador de la carta: va pegado bajo la cabecera y por encima de la fila
       de secciones, que es el orden en que se usan (primero buscas; si no, te
       recorres las secciones). */
    .sf-menu-buscador { background: var(--surf); padding: 12px 0 4px; }
    .sf-menu-buscador .sf-search-wrap { max-width: 100%; }
    .sf-menu-buscador-vacio { margin: 8px 2px 0; font-size: 0.82rem; color: var(--tx2); }

    /* Menú del día. Va en caja y con el precio grande porque es una oferta
       cerrada, no una lista de platos sueltos: lo que decide al cliente es el
       precio, y tiene que verse antes que los nombres. */
    .sf-menudia {
      border: 1.5px solid var(--brd-md);
      border-radius: var(--r-lg);
      padding: 16px 18px;
      margin-bottom: 26px;
      background: var(--surf);
    }
    .sf-menudia-cabecera { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
    .sf-menudia-titulo { font-family: var(--disp); font-size: 1.25rem; margin: 0; flex: 1; }
    .sf-menudia-precio { font-size: 1.25rem; font-weight: 700; white-space: nowrap; }
    .sf-menudia-incluye { font-size: 0.82rem; color: var(--tx2); margin: 6px 0 0; }
    .sf-menudia-grupo { margin-top: 14px; }
    .sf-menudia-grupo-titulo {
      font-size: 0.74rem;
      font-weight: 700;
      letter-spacing: 0.04em;
      text-transform: uppercase;
      color: var(--tx3);
      margin-bottom: 4px;
    }
    .sf-menudia-plato { font-size: 0.9rem; padding: 2px 0; }

    /* Aviso de alérgenos: obligado decir algo cuando no están declarados plato
       a plato, y de todas formas útil cuando sí lo están. */
    .sf-menu-aviso {
      margin: 26px 0 0;
      padding-top: 14px;
      border-top: 1px solid var(--brd);
      font-size: 0.78rem;
      color: var(--tx3);
    }

    /* Hoja de plataformas de reparto: sube desde abajo en el móvil (que es
       donde se pide comida) y se queda centrada en pantalla ancha. */
    .sf-hoja {
      position: fixed;
      inset: 0;
      background: rgba(0, 0, 0, 0.45);
      display: none;
      align-items: flex-end;
      justify-content: center;
      z-index: 1200;
    }
    .sf-hoja.abierta { display: flex; }
    .sf-hoja-caja {
      background: var(--surf);
      width: 100%;
      max-width: 460px;
      border-radius: var(--r-lg) var(--r-lg) 0 0;
      padding: 20px 18px calc(18px + env(safe-area-inset-bottom));
    }
    .sf-hoja-titulo { font-family: var(--disp); font-size: 1.1rem; margin: 0 0 14px; }
    .sf-hoja-cerrar {
      width: 100%;
      background: none;
      border: none;
      padding: 10px;
      color: var(--tx2);
      font-weight: 600;
      font-size: 0.86rem;
      cursor: pointer;
    }
    @media (min-width: 576px) {
      .sf-hoja { align-items: center; }
      .sf-hoja-caja { border-radius: var(--r-lg); }
    }

    /* — WhatsApp flotante + chatbox — */
    .btn-wa-float {
      position: fixed;
      bottom: 22px;
      right: 18px;
      z-index: 190;
      width: 52px;
      height: 52px;
      border-radius: 50%;
      background: #25d366;
      border: none;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 0;
      box-shadow: 0 4px 16px rgba(37, 211, 102, 0.4);
      transition: transform 0.2s, box-shadow 0.2s;
    }
    .btn-wa-float img { width: 30px; height: 30px; }
    .btn-wa-float:hover { transform: scale(1.1); box-shadow: 0 8px 24px rgba(37, 211, 102, 0.5); }

    .wa-chatbox {
      display: none;
      position: fixed;
      bottom: 86px;
      right: 18px;
      z-index: 191;
      width: 300px;
      max-width: calc(100vw - 36px);
      background: var(--surf);
      border-radius: var(--r-lg);
      box-shadow: var(--sh-md);
      overflow: hidden;
    }
    .wa-chatbox.is-open { display: block; }
    .wa-chatbox-header {
      background: #25d366;
      color: #fff;
      padding: 12px 14px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      font-weight: 600;
      font-size: 0.9rem;
    }
    .wa-chatbox-body { padding: 16px 14px; font-size: 0.86rem; color: var(--tx2); }
    .wa-chatbox-body p { margin-bottom: 12px; }
    .btn-wa {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      background: #25d366;
      color: #fff;
      border: none;
      border-radius: var(--r-pill);
      padding: 10px 16px;
      font-weight: 600;
      font-size: 0.86rem;
      text-decoration: none;
    }
    .btn-wa:hover { filter: brightness(0.95); color: #fff; }

    /* — Actividades con plazas (app actividades) — */
    /* Mobile-first: una tarjeta por fila en el móvil (que es donde se apunta
       la gente, de pie en la playa o en el sofá) y dos a partir de tablet.
       Las sesiones son botones grandes porque son LO que se pulsa: la fecha
       no es información, es el botón de comprar. */
    .sf-actividades { padding: 26px 0 48px; }
    .sf-act-lista { display: grid; gap: 16px; }
    @media (min-width: 900px) { .sf-act-lista { grid-template-columns: repeat(2, 1fr); } }

    .sf-act-card {
      background: var(--surf);
      border: 1px solid var(--brd);
      border-radius: var(--r-lg);
      overflow: hidden;
      box-shadow: var(--sh-xs);
    }
    /* Alto fijo y `object-fit: cover`: las fotos de un negocio real vienen en
       vertical y horizontal mezcladas, y sin alto reservado la tarjeta pega un
       salto cuando cada foto termina de cargar. Con esto el hueco ya está
       hecho antes de que llegue la imagen. */
    .sf-act-fotos { position: relative; }
    .sf-act-img { width: 100%; height: 180px; object-fit: cover; display: block; background: var(--bg); }
    /* Hasta cinco: la grande manda y estas la sustituyen. Sin carrusel — son
       cinco fotos, no una galería. */
    .sf-act-minis { display: flex; gap: 6px; padding: 6px; overflow-x: auto; }
    .sf-act-mini {
      flex: 0 0 auto;
      width: 56px;
      height: 42px;
      padding: 0;
      border: 2px solid transparent;
      border-radius: 6px;
      overflow: hidden;
      background: none;
      cursor: pointer;
      opacity: 0.65;
    }
    .sf-act-mini img { width: 100%; height: 100%; object-fit: cover; display: block; }
    .sf-act-mini.activa { border-color: var(--tx); opacity: 1; }
    .sf-act-mini:hover { opacity: 1; }
    .sf-act-modal-foto img {
      width: 100%;
      height: 160px;
      object-fit: cover;
      border-radius: var(--r);
      display: block;
      margin-bottom: 12px;
      background: var(--bg);
    }
    .sf-act-body { padding: 14px 16px 16px; }
    .sf-act-nombre { font-size: 1.05rem; font-weight: 700; margin: 0 0 4px; color: var(--tx); }
    .sf-act-meta { font-size: 0.85rem; color: var(--tx2); margin: 0 0 8px; }
    .sf-act-desc { font-size: 0.88rem; color: var(--tx2); line-height: 1.5; margin: 0 0 10px; }
    .sf-act-punto { font-size: 0.83rem; color: var(--tx2); margin: 0 0 10px; }

    .sf-act-sesiones { display: flex; flex-wrap: wrap; gap: 8px; }
    .sf-act-sesion {
      flex: 1 1 100%;
      display: flex;
      align-items: baseline;
      gap: 8px;
      padding: 10px 12px;
      border: 1px solid var(--brd-md);
      border-radius: var(--r);
      background: var(--surf);
      color: var(--tx);
      text-align: left;
      cursor: pointer;
      font-size: 0.88rem;
    }
    @media (min-width: 520px) { .sf-act-sesion { flex: 1 1 calc(50% - 4px); } }
    .sf-act-sesion:hover:not(:disabled) { border-color: var(--tx); }
    .sf-act-sesion-dia { flex: 1; text-transform: capitalize; }
    .sf-act-sesion-hora { font-weight: 700; }
    .sf-act-sesion-plazas { font-size: 0.78rem; color: var(--tx2); white-space: nowrap; }
    /* Llena: se deja a la vista en gris en vez de esconderla — que la sesión
       del sábado esté completa es información útil (dice que hay que ir al
       otro día), y si desaparece parece que no existe. */
    .sf-act-sesion-llena { opacity: 0.5; cursor: not-allowed; }

    /* Lista de espera. Solo sale cuando NO queda ninguna plaza en ninguna
       fecha (ver taEsperaHTML), asi que es la unica accion que le queda a
       quien esta mirando: va a ancho completo y con el estilo de un boton
       principal, debajo de las fechas ya grises. */
    .sf-act-espera-btn {
      display: block;
      width: 100%;
      margin-top: 10px;
      padding: 11px 14px;
      border: 1px solid var(--tx);
      border-radius: var(--r);
      background: var(--surf);
      color: var(--tx);
      font-size: 0.9rem;
      font-weight: 700;
      cursor: pointer;
    }
    .sf-act-espera-btn:hover { background: var(--tx); color: var(--surf); }

    /* Formulario de inscripción */
    .sf-act-modal {
      display: none;
      position: fixed;
      inset: 0;
      z-index: 1080;
      background: rgba(0, 0, 0, 0.45);
      padding: 16px;
      overflow-y: auto;
    }
    .sf-act-modal.abierto { display: flex; align-items: flex-start; justify-content: center; }
    .sf-act-modal-caja {
      position: relative;
      background: var(--surf);
      border-radius: var(--r-xl);
      padding: 22px 20px;
      width: 100%;
      max-width: 460px;
      margin: auto;
      box-shadow: var(--sh-md);
    }
    .sf-act-modal-caja h3 { font-size: 1.15rem; font-weight: 700; margin: 0 6px 6px 0; color: var(--tx); }
    .sf-act-modal-cerrar {
      position: absolute;
      top: 10px;
      right: 12px;
      border: 0;
      background: none;
      font-size: 1.5rem;
      line-height: 1;
      color: var(--tx3);
      cursor: pointer;
    }
    .sf-act-modal-cuando { font-size: 0.9rem; color: var(--tx2); margin: 0 0 2px; text-transform: capitalize; }
    .sf-act-modal-precio { font-size: 0.88rem; color: var(--tx); font-weight: 600; margin: 0 0 14px; }

    .sf-act-campo { margin-bottom: 12px; }
    .sf-act-campo label { display: block; font-size: 0.84rem; color: var(--tx2); margin-bottom: 4px; }
    .sf-act-campo input,
    .sf-act-campo select,
    .sf-act-campo textarea {
      width: 100%;
      padding: 10px 12px;
      border: 1px solid var(--brd-md);
      border-radius: var(--r);
      background: var(--surf);
      color: var(--tx);
      font-size: 0.95rem;
    }
    .sf-act-campo-check label { display: flex; align-items: center; gap: 8px; color: var(--tx); font-size: 0.9rem; }
    .sf-act-campo-check input { width: auto; }
    .sf-act-req { color: var(--red); }
    .sf-act-aviso {
      background: var(--red-lt);
      border: 1px solid var(--red-mid);
      color: var(--tx);
      border-radius: var(--r);
      padding: 10px 12px;
      font-size: 0.86rem;
      margin-bottom: 12px;
    }
    .sf-act-exito { text-align: center; }
    /* El código va grande y separado: es lo que la persona enseña en la puerta
       y lo que va a mirar con el móvil en la mano, a veces con sol de cara. */
    .sf-act-codigo {
      font-size: 1.8rem;
      font-weight: 700;
      letter-spacing: 3px;
      color: var(--tx);
      margin: 10px 0 14px;
    }
    .sf-act-exito-nota { font-size: 0.82rem; color: var(--tx2); margin-bottom: 16px; }

    /* — Galería de fotos (app galeria) — */
    /* Mosaico de columnas (masonry con CSS `columns`, sin JS ni librería):
       las fotos de un negocio real vienen en vertical y horizontal mezcladas,
       y una rejilla de celdas fijas las recorta todas a cuadrado. Aquí cada
       foto se ve entera y con su proporción. */
    .sf-galeria { padding: 26px 0 48px; }
    /* Buscador de la galería: solo sale con muchas fotos (ver
       TG_MIN_BUSCADOR en js/tienda-galeria.js). Reusa .sf-search-wrap/-input
       del buscador de la carta — mismo gesto, mismo aspecto. */
    .sf-galeria-buscador { margin: 0 0 10px; }
    .sf-galeria-buscador .sf-search-wrap { max-width: 100%; margin: 0; }
    /* Filtros por categoría. Reusan .sf-chip (el mismo control que las
       categorías del catálogo) y hacen scroll lateral en móvil en vez de
       partirse en tres filas, que es lo que ya hace .sf-catbar. */
    .sf-galeria-chips {
      display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; margin: 0 0 14px;
      scrollbar-width: none;
    }
    .sf-galeria-chips::-webkit-scrollbar { display: none; }
    .sf-galeria-grid { columns: 2; column-gap: 12px; }
    @media (min-width: 768px) { .sf-galeria-grid { columns: 3; } }
    @media (min-width: 1200px) { .sf-galeria-grid { columns: 4; } }
    .sf-galeria-item {
      break-inside: avoid;
      margin: 0 0 12px;
      border-radius: var(--r-lg);
      overflow: hidden;
      background: var(--surf);
      box-shadow: var(--sh-xs);
      cursor: zoom-in;
    }
    .sf-galeria-item img { width: 100%; height: auto; display: block; }
    .sf-galeria-pie {
      padding: 8px 12px 10px;
      font-size: 0.82rem;
      color: var(--tx2);
      line-height: 1.4;
    }
    .sf-galeria-pie strong { color: var(--tx); font-weight: 600; }

    /* Visor a pantalla completa. Propio y no un modal de Bootstrap porque
       aquí no hay caja ni cabecera: la foto se ve sobre negro y se cierra
       tocando en cualquier sitio, que es lo que la gente espera de una foto
       ampliada en el móvil. */
    .sf-lightbox {
      position: fixed;
      inset: 0;
      z-index: 2000;
      background: rgba(0, 0, 0, 0.92);
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 24px 16px;
      cursor: zoom-out;
    }
    .sf-lightbox img {
      max-width: 100%;
      max-height: 82vh;
      object-fit: contain;
      border-radius: var(--r);
    }
    .sf-lightbox-pie {
      color: #fff;
      font-size: 0.86rem;
      text-align: center;
      margin-top: 14px;
      max-width: 640px;
    }
    .sf-lightbox-cerrar {
      position: absolute;
      top: 14px;
      right: 16px;
      background: none;
      border: none;
      color: #fff;
      font-size: 2rem;
      line-height: 1;
      opacity: 0.85;
    }

    /* — Alquiler vacacional (app alojamiento) — */
    /* Mobile-first: una casa por fila en el móvil, dos desde tablet y tres en
       pantalla ancha. La foto manda —es lo que hace elegir una casa— y debajo
       va lo que se compara: plazas, dormitorios, baños y precio por noche.

       El buscador de fechas va ARRIBA DEL TODO, pegado bajo el título, porque
       es la pregunta con la que llega quien busca alojamiento: si está libre
       su semana. Filtra en el navegador con lo que ya se trajo, así que
       contesta en el mismo momento en que se toca un día. */
    /* --- Visor de fotos (js/tienda-visor-fotos.js) ---
       Compartido por Alojamiento y Anuncios: una grande, flechas encima a los
       lados y las miniaturas debajo. Las flechas van DENTRO de la foto y no
       fuera para que en movil no roben ancho a la imagen, que es lo que se
       ha venido a ver. */
    .sf-visor { margin-bottom: 14px; }
    .sf-visor-marco { position: relative; background: var(--bg); border-radius: var(--r-lg); overflow: hidden; }
    .sf-visor-grande { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
    .sf-visor-flecha {
      position: absolute; top: 50%; transform: translateY(-50%);
      width: 38px; height: 38px; border-radius: 50%; border: none;
      background: rgba(255,255,255,.92); color: #111; font-size: 1.5rem; line-height: 1;
      display: grid; place-items: center; cursor: pointer; box-shadow: 0 2px 10px rgba(0,0,0,.18);
      transition: background .15s;
    }
    .sf-visor-flecha:hover { background: #fff; }
    .sf-visor-antes { left: 10px; }
    .sf-visor-despues { right: 10px; }
    .sf-visor-cuenta {
      position: absolute; right: 10px; bottom: 10px;
      background: rgba(0,0,0,.6); color: #fff; font-size: .72rem; font-weight: 700;
      padding: 3px 9px; border-radius: 100px;
    }
    .sf-visor-minis { display: flex; gap: 8px; overflow-x: auto; padding: 10px 0 2px; scrollbar-width: none; }
    .sf-visor-minis::-webkit-scrollbar { display: none; }
    .sf-visor-mini {
      width: 66px; height: 50px; object-fit: cover; border-radius: 8px; cursor: pointer;
      flex-shrink: 0; border: 2px solid transparent; opacity: .65; transition: opacity .15s, border-color .15s;
    }
    .sf-visor-mini:hover { opacity: 1; }
    .sf-visor-mini.es-activa { opacity: 1; border-color: var(--tx); }

    /* --- Anuncios (inmuebles) ---
       Rejilla de tarjetas con foto grande: un piso se elige por la foto y el
       precio, en ese orden. Mobile-first, como el resto del escaparate. */
    .sf-anuncios { padding: 26px 0 48px; }
    .sf-anuncios-grid {
      display: grid; gap: 14px;
      grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
    }
    .sf-anuncio {
      background: var(--surf); border: 1px solid var(--brd); border-radius: var(--r-lg);
      overflow: hidden; cursor: pointer; display: flex; flex-direction: column;
      transition: border-color .15s, box-shadow .15s, transform .15s;
    }
    @media (hover: hover) {
      .sf-anuncio:hover { border-color: var(--tx3); box-shadow: 0 8px 24px rgba(0,0,0,.07); transform: translateY(-2px); }
    }
    /* El vendido se lee, pero apagado: sigue ahi como prueba de que la
       agencia vende, sin competir con lo que si esta disponible. */
    .sf-anuncio.es-vendido { opacity: .72; }
    .sf-anuncio-foto { position: relative; aspect-ratio: 4 / 3; background: var(--bg); display: grid; place-items: center; }
    .sf-anuncio-foto img { width: 100%; height: 100%; object-fit: cover; display: block; }
    .sf-anuncio-sinfoto { font-size: 2.4rem; opacity: .35; }
    .sf-anuncio-cinta {
      position: absolute; top: 10px; left: 10px; padding: 3px 10px; border-radius: 100px;
      font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .04em;
      background: var(--tx); color: var(--surf);
    }
    .sf-anuncio-reservado { background: #b45309; color: #fff; }
    .sf-anuncio-cuerpo { padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 5px; flex: 1; }
    .sf-anuncio-titulo { font-size: .92rem; font-weight: 700; margin: 0; line-height: 1.3; color: var(--tx); }
    .sf-anuncio-zona { font-size: .76rem; color: var(--tx2); }
    .sf-anuncio-datos { display: flex; flex-wrap: wrap; gap: 8px; font-size: .74rem; color: var(--tx2); }
    .sf-anuncio-precio { margin-top: auto; padding-top: 6px; font-size: 1.05rem; font-weight: 800; color: var(--tx); }

    /* Ficha completa: capa a pantalla completa, como el visor de la galeria. */
    .sf-anuncio-ficha {
      position: fixed; inset: 0; z-index: 1200; background: rgba(0,0,0,.6);
      display: flex; align-items: center; justify-content: center; padding: 16px; overflow-y: auto;
    }
    .sf-anuncio-ficha-caja {
      background: var(--surf); border-radius: var(--r-lg); max-width: 760px; width: 100%;
      max-height: calc(100vh - 32px); overflow-y: auto; position: relative;
    }
    .sf-anuncio-cerrar {
      position: absolute; top: 10px; right: 10px; z-index: 2;
      width: 34px; height: 34px; border-radius: 50%; border: none;
      background: rgba(0,0,0,.55); color: #fff; font-size: 1.3rem; line-height: 1; cursor: pointer;
    }
    .sf-anuncio-ficha-fotos { display: grid; gap: 2px; grid-template-columns: 1fr 1fr; background: var(--bg); }
    .sf-anuncio-ficha-fotos img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; }
    .sf-anuncio-ficha-fotos img:first-child:last-child,
    .sf-anuncio-ficha-fotos img:first-child { grid-column: 1 / -1; }
    .sf-anuncio-ficha-cuerpo { padding: 18px 20px 24px; }
    .sf-anuncio-ficha-cuerpo h3 { font-family: var(--disp); font-size: 1.3rem; margin: 0 0 4px; }
    .sf-anuncio-ficha-precio { font-size: 1.35rem; font-weight: 800; margin-bottom: 6px; }
    .sf-anuncio-ref { font-size: .74rem; color: var(--tx3); margin-top: 2px; }
    .sf-anuncio-desc { font-size: .88rem; color: var(--tx2); line-height: 1.55; margin: 12px 0; white-space: pre-wrap; }
    .sf-anuncio-datos-lista {
      display: grid; gap: 8px 18px; grid-template-columns: 1fr 1fr; margin: 14px 0 0;
    }
    @media (min-width: 620px) { .sf-anuncio-datos-lista { grid-template-columns: repeat(3, 1fr); } }
    .sf-anuncio-dato dt { font-size: .68rem; text-transform: uppercase; letter-spacing: .04em; color: var(--tx3); font-weight: 700; }
    .sf-anuncio-dato dd { margin: 0; font-size: .86rem; color: var(--tx); font-weight: 600; }
    .sf-anuncio-llamar {
      display: inline-block; margin-top: 18px; padding: 12px 20px; border-radius: 100px;
      background: var(--tx); color: var(--surf); font-weight: 700; font-size: .86rem; text-decoration: none;
    }

    .sf-alojamiento { padding: 26px 0 48px; }

    .sf-aloj-buscador {
      display: flex;
      flex-wrap: wrap;
      align-items: flex-end;
      gap: 10px 12px;
      background: var(--surf);
      border: 1px solid var(--brd);
      border-radius: var(--r-lg);
      padding: 14px 16px;
      box-shadow: var(--sh-xs);
      margin-bottom: 12px;
    }
    .sf-aloj-campo { flex: 1 1 140px; min-width: 0; }
    .sf-aloj-campo label {
      display: block;
      font-size: 0.72rem;
      font-weight: 700;
      color: var(--tx3);
      text-transform: uppercase;
      letter-spacing: 0.03em;
      margin-bottom: 4px;
    }
    .sf-aloj-campo input {
      width: 100%;
      border: 1px solid var(--brd-md);
      border-radius: var(--r);
      padding: 9px 10px;
      font-size: 0.9rem;
      color: var(--tx);
      background: var(--surf);
      font-family: inherit;
    }
    .sf-aloj-limpiar {
      flex: 0 0 auto;
      border: 1px solid var(--brd-md);
      background: var(--surf);
      color: var(--tx2);
      border-radius: var(--r-pill);
      padding: 9px 14px;
      font-size: 0.82rem;
      font-weight: 600;
      cursor: pointer;
    }
    .sf-aloj-limpiar:hover { border-color: var(--tx); color: var(--tx); }
    .sf-aloj-resumen { font-size: 0.88rem; color: var(--tx2); margin: 0 0 14px; font-weight: 600; }

    .sf-aloj-lista { display: grid; gap: 16px; }
    @media (min-width: 640px) { .sf-aloj-lista { grid-template-columns: repeat(2, 1fr); } }
    @media (min-width: 1100px) { .sf-aloj-lista { grid-template-columns: repeat(3, 1fr); } }

    .sf-aloj-card {
      background: var(--surf);
      border: 1px solid var(--brd);
      border-radius: var(--r-lg);
      overflow: hidden;
      box-shadow: var(--sh-xs);
      cursor: pointer;
      display: flex;
      flex-direction: column;
    }
    .sf-aloj-card:hover { border-color: var(--brd-md); box-shadow: var(--sh-md); }
    .sf-aloj-card:focus-visible { outline: 2px solid var(--tx); outline-offset: 2px; }
    .sf-aloj-foto-wrap { position: relative; }
    /* Alto reservado: las fotos de una casa real vienen en vertical y
       horizontal mezcladas, y sin esto la rejilla pega un salto según van
       cargando. */
    .sf-aloj-foto { width: 100%; height: 190px; object-fit: cover; display: block; background: var(--bg); }
    .sf-aloj-foto-vacia {
      display: flex; align-items: center; justify-content: center;
      font-size: 2.4rem; color: var(--tx3);
    }
    /* La chapa del resultado de la búsqueda. Encima de la foto y no debajo del
       nombre: es LA respuesta a lo que se acaba de preguntar, y ahí se ve sin
       tener que leer la tarjeta. */
    .sf-aloj-chapa {
      position: absolute;
      top: 10px;
      left: 10px;
      border-radius: var(--r-pill);
      padding: 5px 11px;
      font-size: 0.75rem;
      font-weight: 700;
    }
    .sf-aloj-chapa.es-si { background: #dff3e5; color: #12602f; }
    .sf-aloj-chapa.es-no { background: #f7dede; color: #97231f; }
    .sf-aloj-chapa.es-aviso { background: #fbeecd; color: #7a5410; }

    .sf-aloj-body { padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 6px; }
    .sf-aloj-nombre { font-size: 1rem; font-weight: 700; margin: 0; color: var(--tx); }
    .sf-aloj-zona { font-size: 0.8rem; color: var(--tx2); margin: 0; }
    /* La fila de datos: iconos con su número. Es lo que sustituye al párrafo
       de "4 huéspedes · 2 dormitorios · 1 baño · 78 m²" — misma información,
       reconocible sin leerla, y ahora en columnas de verdad (ver
       LodgingProperty), así que además se puede filtrar. El título de cada uno
       lleva la palabra entera: un icono que alguien no reconoce y no puede
       consultar es peor que la palabra. */
    .sf-aloj-datos { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 0.83rem; color: var(--tx2); }
    .sf-aloj-dato { display: inline-flex; align-items: center; gap: 4px; }
    .sf-aloj-dato i { font-style: normal; }
    .sf-aloj-datos-grande { font-size: 0.92rem; gap: 6px 16px; margin: 10px 0; }

    .sf-aloj-extras { display: flex; flex-wrap: wrap; gap: 5px; }
    .sf-aloj-extra {
      background: var(--bg);
      border-radius: var(--r-pill);
      padding: 3px 9px;
      font-size: 0.74rem;
      color: var(--tx2);
      white-space: nowrap;
    }
    .sf-aloj-extra-mas { font-weight: 700; }
    .sf-aloj-extras-todos { margin: 12px 0; gap: 6px; }
    .sf-aloj-extras-todos .sf-aloj-extra { font-size: 0.8rem; padding: 5px 11px; }

    .sf-aloj-precio { margin: 2px 0 0; font-size: 0.9rem; color: var(--tx); }
    .sf-aloj-precio strong { font-size: 1.05rem; }
    .sf-aloj-precio span { color: var(--tx2); font-size: 0.82rem; }
    .sf-aloj-min { color: var(--tx3); }
    .sf-aloj-total { margin: 0; font-size: 0.82rem; color: var(--tx2); font-weight: 600; }

    /* La ficha de la casa. Modal propio y no el de producto: allí manda el
       precio y el botón de la cesta, aquí mandan las fechas. */
    .sf-aloj-modal {
      display: none;
      position: fixed;
      inset: 0;
      z-index: 1080;
      background: rgba(0, 0, 0, 0.45);
      padding: 16px;
      overflow-y: auto;
    }
    .sf-aloj-modal.abierto { display: flex; align-items: flex-start; justify-content: center; }
    .sf-aloj-modal-caja {
      position: relative;
      background: var(--surf);
      border-radius: var(--r-xl);
      padding: 20px;
      width: 100%;
      max-width: 560px;
      margin: auto;
      box-shadow: var(--sh-md);
    }
    .sf-aloj-modal-cerrar {
      position: absolute;
      top: 10px;
      right: 12px;
      border: 0;
      background: rgba(255, 255, 255, 0.85);
      border-radius: 50%;
      width: 32px;
      height: 32px;
      font-size: 1.4rem;
      line-height: 1;
      color: var(--tx2);
      cursor: pointer;
      z-index: 1;
    }
    .sf-aloj-modal-caja h3 { font-size: 1.2rem; font-weight: 700; margin: 0 30px 4px 0; color: var(--tx); }
    .sf-aloj-ficha-grande {
      width: 100%;
      height: 240px;
      object-fit: cover;
      border-radius: var(--r);
      display: block;
      background: var(--bg);
    }
    .sf-aloj-minis { display: flex; gap: 6px; padding: 6px 0 10px; overflow-x: auto; }
    .sf-aloj-mini {
      width: 62px; height: 48px; object-fit: cover; border-radius: 8px;
      cursor: pointer; flex-shrink: 0; opacity: 0.6;
      border: 2px solid transparent;
    }
    .sf-aloj-mini.es-activa, .sf-aloj-mini:hover { opacity: 1; border-color: var(--tx); }
    .sf-aloj-ficha-precio { margin: 8px 0 0; font-size: 1rem; color: var(--tx); }
    .sf-aloj-ficha-precio strong { font-size: 1.25rem; }
    /* La descripción de una casa son renglones (equipamiento, condiciones), no
       una frase: se respetan los saltos de línea — mismo motivo que en la
       ficha de producto. */
    .sf-aloj-desc { font-size: 0.9rem; color: var(--tx2); line-height: 1.55; white-space: pre-line; margin: 10px 0; }
    .sf-aloj-horarios { font-size: 0.85rem; color: var(--tx2); margin: 10px 0 0; }
    /* Llamar es LA acción de esta pantalla: aquí no se reserva, y esconder el
       teléfono en una línea de texto gris sería dejar el recorrido a medias. */
    .sf-aloj-llamar {
      display: block;
      margin-top: 12px;
      text-align: center;
      background: var(--tx);
      color: #fff;
      border-radius: var(--r-pill);
      padding: 12px 16px;
      font-weight: 700;
      font-size: 0.92rem;
      text-decoration: none;
    }
    .sf-aloj-llamar:hover { color: #fff; filter: brightness(1.15); }

    /* Reservar y pagar. Solo sale cuando la casa lo admite y el negocio tiene
       el cobro conectado — ver `reservable` en la respuesta pública. */
    .sf-aloj-reserva { margin-top: 16px; border-top: 1px solid var(--bd); padding-top: 14px; }
    .sf-aloj-reserva h4 { font-size: 1rem; margin: 0 0 10px; }
    .sf-aloj-reserva label {
      display: block;
      font-size: 0.8rem;
      font-weight: 700;
      color: var(--tx2);
      margin-bottom: 10px;
    }
    .sf-aloj-reserva input {
      display: block;
      width: 100%;
      margin-top: 4px;
      padding: 10px 12px;
      border: 1px solid var(--bd);
      border-radius: 10px;
      font: inherit;
      font-weight: 400;
      color: var(--tx);
      background: #fff;
    }
    /* Entrada y salida caben en una fila salvo en pantallas muy estrechas:
       son la misma decisión y verlas juntas evita elegir una salida anterior
       a la entrada. */
    .sf-aloj-reserva-fechas { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
    @media (max-width: 360px) { .sf-aloj-reserva-fechas { grid-template-columns: 1fr; } }
    .sf-aloj-desglose {
      background: var(--bg2, #f7f7f8);
      border-radius: 10px;
      padding: 10px 12px;
      font-size: 0.88rem;
      margin: 4px 0 12px;
    }
    .sf-aloj-desglose span { font-size: 0.82rem; color: var(--tx2); }
    .sf-aloj-reserva-error { color: #b23c3c; font-size: 0.85rem; margin-bottom: 10px; }
    .sf-aloj-reservar {
      display: block;
      width: 100%;
      background: var(--tx);
      color: #fff;
      border: 0;
      border-radius: var(--r-pill);
      padding: 13px 16px;
      font-weight: 700;
      font-size: 0.95rem;
      cursor: pointer;
    }
    .sf-aloj-reservar:disabled { opacity: 0.6; cursor: default; }
    .sf-aloj-reserva-nota { font-size: 0.72rem; color: var(--tx3); text-align: center; margin: 10px 0 0; }


    /* — Presupuestos (app presupuestos) — */
    /* Rejilla de tarjetas: cada una es una cosa que este negocio puede
       presupuestar, y abre SU formulario. Se parece a .sf-tile (productos) a
       propósito, porque para el cliente es lo mismo — mirar lo que ofrecen y
       pulsar uno — pero sin precio ni chapa de oferta: aquí el precio es
       justamente lo que se va a preguntar. */
    .sf-presupuestos { padding: 26px 0 48px; }
    .sf-pr-intro { color: var(--tx2); font-size: 0.92rem; margin: -6px 0 18px; }
    .sf-pr-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 12px;
    }
    @media (min-width: 700px) { .sf-pr-grid { grid-template-columns: repeat(3, 1fr); } }
    @media (min-width: 1100px) { .sf-pr-grid { grid-template-columns: repeat(4, 1fr); } }

    .sf-pr-card {
      display: flex;
      flex-direction: column;
      text-align: left;
      width: 100%;
      padding: 0;
      background: var(--surf);
      border: 1px solid var(--brd);
      border-radius: var(--r-lg);
      overflow: hidden;
      box-shadow: var(--sh-xs);
      cursor: pointer;
      transition: transform 0.12s ease, box-shadow 0.12s ease;
    }
    .sf-pr-card:hover { transform: translateY(-2px); box-shadow: var(--sh-sm); }

    /* Alto fijo y `cover`: las fotos de un banco vienen en proporciones
       distintas y sin esto la rejilla queda con los pies de tarjeta a
       alturas diferentes, que es lo que hace que veinte tarjetas parezcan
       desordenadas. */
    .sf-pr-img {
      width: 100%;
      height: 118px;
      object-fit: cover;
      display: block;
      background: var(--bg2, #f3f2f0);
    }
    /* Sin foto la tarjeta no se rompe: se queda una banda con la inicial, en
       vez de un hueco en blanco de 118 px. */
    .sf-pr-img-vacia {
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 1.8rem;
      color: var(--tx3);
      background: var(--bg2, #f3f2f0);
    }
    .sf-pr-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 4px; flex: 1; }
    .sf-pr-nombre { font-size: 0.92rem; font-weight: 700; color: var(--tx); margin: 0; line-height: 1.3; }
    .sf-pr-desc {
      font-size: 0.8rem;
      color: var(--tx2);
      line-height: 1.4;
      margin: 0;
      /* Dos renglones y puntos suspensivos: las descripciones vienen de
         distintas longitudes y sin el corte una tarjeta estira toda su fila. */
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
      overflow: hidden;
    }
    .sf-pr-pedir {
      margin-top: auto;
      padding-top: 8px;
      font-size: 0.82rem;
      font-weight: 700;
      color: var(--tx);
    }
