/* ============================================================================
   estructura-base.css — CAPA DE APARIENCIA COMPARTIDA DE LA FLOTA
   ----------------------------------------------------------------------------
   Extraído de Hippie Chippy (jul 2026). Da a todas las apps el MISMO aspecto
   estructural (headers transparentes, aside degradado, tipografía, espaciados)
   escrito ENTERAMENTE con variables (`var(--...)`). Ni un color ni una fuente a
   pelo: todo sale de tokens que aporta cada app (su identidad).

   FILOSOFÍA: la base pone la PIEL (fondos, blur, sombra, espaciado, tipografía);
   cada app pone su COLOR/FUENTE (tokens) y mantiene su LAYOUT (posición, ancho,
   alto, grid/flex propios). Por eso la base NO toca `position`, `width`, `height`
   ni `z-index`: eso es layout de cada app y romperlo es fácil. Solo apariencia.

   Los iconos de navegación y los botones del index NO están aquí: pertenecen a
   la receta de iconos (SUPER_APP/RECETA_ICONOS), que se aplica antes.

   Orden de carga recomendado (ver CONTRATO-TOKENS.md):
     reset.css → [css de la app con sus tokens] → estructura-base.css
   La base va LA ÚLTIMA para imponer la apariencia común por encima de los
   fondos sólidos que cada app tuviera. Quitar el <link> revierte todo.
   ============================================================================ */

/* ============================================================================
   COMPONENTES DE APP — formulario de reservas, botones de reserva, botón de
   WhatsApp, avisos, luces y ajustes de html/body/p/i.
   ----------------------------------------------------------------------------
   Vivían en el styles.css de CADA app, repetidos ~56 veces, aunque son
   IDÉNTICOS en toda la flota: lo único que cambia son los tokens del tema. Esa
   mezcla hacía que rehacer un tema se llevara por delante el formulario entero
   (pasó alineando Trufarella, ago 2026: el form salía desmaquetado y el botón
   RESERVAS sin pastilla). Subidos aquí, `styles.css` queda como debe ser: SOLO
   el :root con los colores del cliente.

   Van al PRINCIPIO del archivo A PROPÓSITO: antes se cargaban desde styles.css,
   que va ANTES que esta base, así que ponerlos aquí arriba conserva EXACTAMENTE
   el mismo orden de cascada — el resto de la base los sigue sobreescribiendo
   igual que antes.
   ============================================================================ */

html {
  color: var(--color-texto-tema);
  /* Evita el scroll horizontal en toda la app. El logo animado del index
     (.movimiento) está en position:fixed y hace scale(2), lo que lo desborda
     por el lateral y arrastraba el resto del contenido. Recortar en la raíz
     es seguro: no afecta a los elementos fixed visibles (el formulario de
     reservas entra dentro del viewport) ni al scroll vertical. */
  overflow-x: hidden;
}
body {
  width: 100%;
  /* La tipografía (familia + tamaño + interlineado) va TODA junta en el bloque
     TIPOGRAFÍA BASE de más abajo. Estaba también aquí, duplicada. */
  /* Fondo negro de base: evita destellos blancos al hacer overscroll en móvil
     (el rebote de iOS muestra el fondo del documento) y unifica con el
     theme-color negro de la barra del navegador. */
  background-color: var(--fondo-negro);
}

/* .visually-hidden vive UNA sola vez, junto a la TIPOGRAFÍA BASE (más abajo).
   Estaba declarada dos veces, idéntica, en esta misma hoja. */

/* fadeIn - En el <body> de cada página */
.fade-in {
  animation: fadeIn ease 2s;
}
@keyframes fadeIn {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
/* Títulos de las familias de comidas y bebidas */
p {
  font-size: 1rem;
  /* white-space: pre-line; Valida los saltos de linea \n que inserto en los textos largos que tengo en la data*/
  white-space: pre-line;
  color: var(--color-texto-tema);
}
h2 {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--color-texto-tema);
}
h3 {
  font-size: 1rem;
  color: var(--color-texto-tema);
}
h4 {
  color: var(--color-texto-tema); /* tamaño y padding: estructura-base */
}

/* ICONOS BOOTSTRAP */
i {
  font-size: 1.7rem;
  padding: 0 0.8rem;
  /* color: var(--color-secundario); */
}
/* Iconos Cartas */
.bi-house-fill,
.bi-heart-pulse,
/*  */
/* ******************** FOOTER ****************************** */
/* FOOTER. Da estilos a todos los footers */

/* El estilo de .baner__alergenos vive en cartas.css (única definición),
   ya que el banner solo aparece en carta.html y drinks.html. */
.footer__imagen-alergenos {
  width: 90%;
}
.footer__boton-alergenos {
  width: auto;
  border-radius: var(--border-radius);
  margin: 0 auto;
  padding: 0.3rem 1rem;
  text-align: center;
  box-shadow: var(--sombra);
  color: var(--color-texto-tema);
  /* Degradado de marca por token (convención): en apps CLARAS pinta el
     gradiente sobre el cristal; en OSCURAS el token es 'none' → sin efecto. */
  background-image: var(--color-degradado2);
}


/* Fondo interior (.logo__fondo + media query) ahora en estructura-base.css */

/* ***************** LOGO (imagen transparente) *********** */
/* Logos limpios: lo gobierna la BASE (.hero-img--logo, con la feature opt-in
   .hero-img--marco para enmarcarlos). Se retiró la regla img[src*="logo"]
   local: su especificidad de atributo pisaba el marco de la base. */
/* ***************** ANIMACIONES *************************** */
@keyframes rotation {
  from {
    transform: rotateX(180deg);
  }
  to {
    transform: rotateX(360deg);
  }
}

/* ***************** LUCES QUE BRILLAN ********************* */
/* Campo de puntos de luz que parpadean por toda la pantalla (mismo efecto
   que en otras apps de la flota, p.ej. Peaky Blinders). Los <div> los genera
   js/luces.js y el efecto se activa/desactiva desde modulos.luces.
   pointer-events:none para que nunca intercepten clics. */
@keyframes parpadeo-luz {
  0% {
    width: 1px;
    height: 1px;
    background: powderblue;
    box-shadow: 0 0 0.8125rem #fff;
  }
  10% {
    background: #b0e5de;
  }
  20% {
    background: #b0e5cc;
  }
  30% {
    background: #cce5b0;
  }
  40% {
    background: #dae5b0;
  }
  50% {
    width: 0.25rem;
    height: 0.25rem;
    background: #e5ddb0;
    box-shadow: 0 0 0.8125rem #d3bd3d;
  }
  100% {
    width: 1px;
    height: 1px;
    background: powderblue;
    box-shadow: 0 0 0.8125rem #fff;
  }
}
.luz {
  position: fixed;
  width: 1px;
  height: 1px;
  border-radius: 0.3125rem;
  background: powderblue;
  box-shadow: 0 0 0.8125rem #fff;
  pointer-events: none;
}
.luz.activa {
  animation: parpadeo-luz 2s infinite;
}

/* ********* FORMULARIO RESERVAS *************************** */
.form {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 3;
  width: 90vw;
  max-width: 35rem;
  height: 100vh;
  padding: 2.5rem 2rem;
  background: var(--form-fondo);
  border-right: 0.125rem solid var(--color-primario);
  transition: all 0.5s ease-in-out;
  overflow-y: auto;
  /* Ocultar menú lateral */
  opacity: 0;
  visibility: hidden;
  transform: translateX(-100%);
  box-shadow: 1.875rem 0.625rem 2.5rem rgba(0, 0, 0, 0.7);
}
.form.active {
  opacity: 1;
  visibility: visible;
  transform: translateX(0);
}
.form div {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.form label {
  font-size: 1rem;
  font-weight: 600;
  color: var(--form-texto);
  display: inline-block;
  margin: 0.5rem 0 0.2rem;
}
.input-small {
  width: 65%;
}
.form .form__fila {
  flex-direction: row;
  gap: 1rem;
  /* Alinea los inputs al pie de la fila para que queden a la misma altura
     aunque una label ocupe 1 línea y otra 2 (ej. "Teléfono" vs "Fecha de la reserva") */
  align-items: flex-end;

  & > div {
    flex: 1;
    min-width: 0;
  }

  & .input-small {
    width: 100%;
  }
}
.form input,
.form select {
  padding: 0.6rem 0.8rem;
  background: var(--form-input-fondo);
  border: 1px solid var(--color-form-borde);
  border-radius: 0.625rem;
  font-size: 1rem;
  color: var(--form-texto);
  transition: border-color 0.3s ease;
}
.form input::placeholder {
  color: var(--form-placeholder);
}
.form input[type="date"]::-webkit-calendar-picker-indicator {
  filter: var(--form-datepicker-filtro);
}
.form input:focus,
.form select:focus,
.form textarea:focus {
  outline: none;
  border-color: var(--color-primario);
  box-shadow: 0 0 0.5rem var(--color-form-borde-focus);
}
.form textarea {
  width: 100%;
  padding: 0.6rem 0.8rem;
  background: var(--form-input-fondo);
  border: 1px solid var(--color-form-borde);
  border-radius: 0.625rem;
  font-size: 1rem;
  color: var(--form-texto);
  transition: border-color 0.3s ease;
  resize: vertical;
}
.form select {
  cursor: pointer;
}
.form select option {
  color: var(--color-secundario);
}
.form span {
  color: var(--form-placeholder);
  font-size: 0.85rem;
}
.warning {
  /* Aviso del formulario: color tema-aware (claro sobre panel oscuro, oscuro
     legible sobre panel claro). */
  color: var(--form-warning);
  font-weight: 600;
  opacity: 0;
  padding: 0.5rem 0;
  transition: all 0.7s ease-in-out;
  transform: translateX(-100%);
}
.active-warning {
  opacity: 1;
  transform: translateX(0);
}
/* Guard del formulario: el JS genera el <p> "aviso" sin clase. Lo forzamos al
   color de texto del formulario (tema-aware) para que el guard no dependa de la
   herencia y se lea en ambos temas. */
.form p {
  color: var(--form-texto);
}
.warning p {
  color: var(--form-warning);
}
.reservar {
  width: fit-content;
  margin: 1.5rem auto 0;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 0.5rem 1.5rem;
  font-size: 1.5rem;
  font-weight: 800;
  text-decoration: none;
  border-radius: var(--border-radius);
  cursor: pointer;
}

/* ********* BOTONES DE RESERVA CON DEGRADADO ANIMADO *************************** */
/* Aplicado a los 3 botones de reserva: header de index, header de carta/drinks
   y botón final del formulario. El degradado se mueve horizontalmente en bucle
   para llamar la atención sobre la acción principal del SaaS. */
.index__boton-reservar,
.carta__boton-reservar,
.reservar {
  color: var(--blanco);
  background: linear-gradient(
    270deg,
    var(--color-secundario),
    var(--color-primario),
    color-mix(in srgb, var(--color-primario) 30%, var(--color-secundario)),
    var(--color-secundario)
  );
  /* 300% = (4 paradas - 1) × 100%. Crea espacio para que background-position se mueva */
  background-size: 300% 100%;
  border: 1px solid var(--blanco);
  box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.2);
  text-shadow: none;
  animation: degradadoMovil 3s ease infinite;
  transition:
    transform 0.3s ease,
    box-shadow 0.3s ease;

  &:hover {
    transform: scale(1.05);
    box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.3);
  }
}

@keyframes degradadoMovil {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}

/* Cerrar formulario de reservas */
.cerrar-formulario {
  position: absolute;
  top: 0.8rem;
  right: 1rem;
  /* Disco verde con la X en blanco: el glifo es solo la X (bi-x-lg) y el
     círculo lo pone el background, así la X se ve blanca (antes era el hueco
     transparente de bi-x-circle-fill y mostraba el fondo oscuro). */
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.2rem;
  height: 2.2rem;
  padding: 0; /* anula el padding del selector global `i` (si no, sería un óvalo) */
  font-size: 1.4rem;
  color: var(--blanco);
  background: var(--color-primario);
  border-radius: 50%;
  cursor: pointer;
  transition: transform 0.3s ease;
}
.cerrar-formulario:hover {
  transform: scale(1.2);
}
.cerrar-formulario-desde-fuera {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 2;
}

/* Position absolute */
/* Botón Llamada */
.boton-whatsapp {
  display: none;
  justify-content: center;
  align-items: center;
  position: fixed;
  bottom: 1rem;
  left: 50%;
  padding: 0.3rem 2rem;
  transform: translate(-45%, -50%);
  font-size: 1.2rem;
  border: 1px solid var(--blanco);
  border-radius: 1.25rem;
  box-shadow: var(--sombra);
  cursor: pointer;
  z-index: 2;
}
.booking {
  color: var(--blanco);
  text-shadow: none;
}
#booking {
  color: var(--blanco);
  font-size: 1.5rem;
  font-weight: bold;
}
.boton-whatsapp-img {
  width: 2rem;
}
.boton-whatsapp span {
  font-size: 1.5rem;
  font-weight: bold;
}


/* Animaciones fade-in: en estructura-base (estándar de flota) */



/* ----------------------------------------------------------------------------
   TOKENS ESTRUCTURALES (universales, iguales en toda la flota).
   Valores por defecto; una app puede sobrescribir cualquiera en su :root.
   Los tokens de IDENTIDAD (colores, fuentes, sombra) los aporta la app.
   ---------------------------------------------------------------------------- */
:root {
  /* Escala de tamaños de fuente (de la distribución real de la flota) */
  --fs-xs: 0.8rem;
  --fs-sm: 0.9rem;
  --fs-base: 1rem;
  --fs-md: 1.1rem;
  --fs-lg: 1.2rem;
  --fs-xl: 1.5rem;
  --fs-2xl: 2rem;
  --fs-3xl: 2.8rem;

  /* Grosores */
  --fw-normal: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;

  /* Escala de espaciado (gaps, paddings, márgenes) */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.25rem;
  --sp-6: 1.5rem;
  --sp-8: 2rem;
  --sp-12: 3rem;

  /* Desenfoque de los headers transparentes */
  --blur-header: 0.45rem;

  /* ==== TOKENS DE ESTRUCTURA (medidas estándar de la flota, ref. Hippie Chippy) ====
     Vocabulario ÚNICO de dimensiones. La base impone algunos (aside, header) y
     el resto son OPT-IN: cada app cambia sus números mágicos por estos tokens al
     migrar su carta. Objetivo: que TODAS las apps (actuales y nuevas) acaben con
     la misma estructura. Cambiar un valor aquí = cambiarlo en toda la flota. */

  /* Contenido (columna central de las páginas de carta) */
  --ancho-contenido: 50rem;      /* max-width del bloque de carta */
  --pad-lateral: 0.5rem;         /* padding lateral del contenido */

  /* Aside (barra lateral de iconos).
     NO se declaran aquí: la base los CONSUME con fallback en la regla .aside
     (var(--aside-gap, 1.25rem) / var(--aside-padding, 5rem 0.4rem)). Así una app
     puede sobrescribirlos en SU :root (que carga antes) sin que este archivo,
     al cargar el último, los pise. Default de flota = el valor del fallback. */

  /* Header (barra superior con logo/banderas) */
  --header-gap: 1rem;
  --header-padding: 0.5rem 2rem;

  /* Botones del index (estándar jul 2026 = El Ancla) */
  --btn-index-sep: 1.5rem;                       /* separación ENTRE botones */
  --btn-index-gap: 0.75rem;                      /* icono ↔ texto DENTRO del botón */
  --btn-index-padding: 0.6rem 0.4rem 0.6rem 1.2rem; /* padding interior */

  /* Familias (secciones de la carta) */
  --gap-familias: 0.5rem;        /* separación entre familias */
  --pad-familia: 0.4rem;         /* padding interior de la familia */
  --img-familia: 8rem;           /* alto de la imagen de familia */

  /* Platos */
  --gap-platos: 0.3rem;        /* separación entre platos (única medida) */
  --img-plato: 4rem;             /* tamaño de la miniatura del plato */

  /* Animación de aparición de modales (crecer desde el centro con rebote) */
  --ease-rebote: cubic-bezier(0.34, 1.56, 0.64, 1);
  --modal-crecer-dur: 1s;
}

/* ----------------------------------------------------------------------------
   TIPOGRAFÍA BASE — la FAMILIA la pone la app (--tipo-principal).
   Solo fijamos familia base, tamaño base y altura de línea. Los tamaños de los
   títulos siguen siendo de cada app (se adoptan los --fs-* poco a poco).
   ---------------------------------------------------------------------------- */
body {
  font-family: var(--tipo-principal);
  font-size: var(--fs-base);
  line-height: 1.5;
}

/* Oculta visualmente pero accesible a lectores de pantalla y SEO. El PUNTO_DE_
   PARTIDA exige un <h1 class="visually-hidden"> en cada página, así que la
   clase vive en la BASE (antes cada app la definía en su styles.css y en
   Fontanales faltaba → el h1 se veía en pantalla, jul 2026). */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ----------------------------------------------------------------------------
   HEADERS TRANSPARENTES — quita el fondo sólido y aplica solo desenfoque.
   Cubre las variantes de la flota (cartas/bebidas y alérgenos/reseñas).
   NO toca position/width/z-index: cada app mantiene su colocación.
   ---------------------------------------------------------------------------- */
.main__header,
.main__header2,
/* ----------------------------------------------------------------------------
   FIX iOS/SAFARI (jul 2026): sin color de fondo en html, Safari pinta sus
   barras (superior y de navegación) y el rebote del scroll en blanco/negro
   del sistema, y el contenido se ve POR ENCIMA del header fijo y tras la
   barra. Receta: html toma --fondo-app (token por tema: oscuras #000,
   claras #fff) y los headers fijos llevan un tinte CASI opaco del mismo
   color (85%) + blur: siguen pareciendo cristal pero tapan el contenido.
   Acompañar con <meta name="theme-color"> en cada HTML.
   ---------------------------------------------------------------------------- */
html {
  background-color: var(--fondo-app, #000);
}
/* Las decoraciones con z-index NEGATIVO (ancla flotante .movimiento, fondos
   .logo__fondo…) deben pintarse ENCIMA del background del body, no detrás.
   Sin esto, mientras el body anima su opacity (fadeIn) existe un contexto de
   apilamiento temporal que las muestra… y al terminar la animación caen
   detrás del fondo y "desaparecen" (bug del ancla de El Ancla, jul 2026).
   isolation crea ese contexto de forma PERMANENTE, sin otros efectos. */
body {
  isolation: isolate;
}
.main__header,
.main__header2,
.allergens__header,
.reviews__header {
  /* Los headers son superficie LIQUID GLASS: mismo tinte translúcido del tema
     (--lg-bg-color: ~40-45%) + blur. El 85% del primer fix iOS los volvía
     sólidos (negro en El Ancla); con el blur y el --fondo-app del html basta
     para que el contenido no se lea a través en Safari. */
  background-color: var(--lg-bg-color, color-mix(in srgb, var(--fondo-app, #000) 40%, transparent));
  backdrop-filter: blur(var(--blur-header));
  -webkit-backdrop-filter: blur(var(--blur-header));
  /* Altura FIJA de flota (4rem): el header mide lo mismo lleve botón de
     reservas, logo o solo banderas — el contenido se centra en vertical.
     Antes cada app/página medía distinto (48-61px) según su contenido. */
  height: var(--alto-header, 4rem);
  align-items: center;
}
/* BANDERAS DE IDIOMA — interacción universal de flota (jul 2026).
   El tamaño, el glow y la sombra los pone cada app en su cartas.css; aquí SOLO
   el comportamiento común a toda la flota:
   - cursor: pointer  → afordancia de click (la mano al pasar por encima).
   - user-select/-webkit-user-drag: none → las banderas son <img> y por defecto se
     pueden ARRASTRAR; sin esto, el 1er click podía interpretarse como el inicio de
     un arrastre y no disparaba el cambio de idioma. */
.bandera {
  cursor: pointer;
  user-select: none;
  -webkit-user-drag: none;
}
/* Contenedores de la página de alérgenos (estándar = tarjeta El Ancla):
   ancho, tarjeta con radio y sombra; el cristal y el borde fino los pone
   liquid-glass.css. El COLOR del h1 lo pone cada app. */
.allergens__info {
  width: 90dvw;
  max-width: 50rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin: 2rem auto;
  padding: 1rem;
  border-radius: var(--border-radius);
  box-shadow: var(--sombra);
}
.allergens__info h1 {
  font-size: 1.6rem;
  font-weight: 700;
  text-align: center;
}

/* Tipografía y ritmo de la carta (estándar de flota, jul 2026):
   el TAMAÑO vive aquí; el color lo pone cada app con sus tokens. */
.platos__familia-h2 {
  font-size: 1.5rem;
}
.container h4 {
  font-size: 1.3rem;
  /* Interlineado propio: el container hereda line-height 1rem (menor que la
     fuente) y los títulos a dos líneas quedaban aplastados. 1.15 unitless. */
  line-height: 1.15;
  /* padding inferior SOLO cuando hay descripción debajo (ver :not de abajo) */
  /* COLORES DE CARTA (estándar Manu, jul 2026 — sustituye a la convención
     'acento si hay descripción'): nombres SIEMPRE en blanco/negro según el
     tema (token --color-nombre-plato) y descripciones/precios en el color
     del cliente (token --color-acento-carta). Uniforme, sin alternancias. */
  color: var(--color-nombre-plato, inherit);
}
.container h4.sin-descripcion {
  color: var(--color-nombre-plato, inherit); /* sin distinción de color ya */
}
.container h4:not(.sin-descripcion) {
  padding-bottom: 0.2rem; /* aire nombre→descripción; sin descripción no suma */
}
.product__description {
  color: var(--color-acento-carta, inherit);
  font-size: 1.1rem; /* estándar Ancla */
  padding-left: 1rem; /* sangría respecto al nombre del plato */
  line-height: 1.25; /* descripciones multilínea legibles (mismo mal del container) */
}
.cont__prices {
  color: var(--color-acento-carta, inherit);
}
/* Regla tipográfica de flota (Manu, jul 2026) — por CSS, la data no se toca:
   - COMIDAS y POSTRES: nombre de plato SIEMPRE en mayúsculas.
   - BEBIDAS: nombre en minúscula (tipo frase, con inicial mayúscula)…
     …salvo que el producto tenga descripción → entonces en mayúsculas.
   El hook es .sin-descripcion (la añade cartaMaster cuando no hay descripción).
   Las descripciones nunca se transforman (quedan como en la data). */
#comidas .container h4,
#postres .container h4 {
  text-transform: uppercase;
}
#bebidas .container h4:not(.sin-descripcion) {
  text-transform: uppercase;
}
/* Bebidas sin descripción: la DATA manda (tipo frase con los nombres
   propios bien escritos — "Coca-Cola", "Jägermeister"…). El CSS no fuerza
   minúsculas: no sabe qué es nombre propio. Estándar de escritura: El Ancla. */
#bebidas .container h4.sin-descripcion {
  text-transform: none;
}
/* Subcabeceras dentro de una familia (ej. "VINOS BLANCOS") — estándar de flota:
   mayúsculas y centradas, en el COLOR DEL CLIENTE (decisión Manu jul 2026) —
   destacan como separador; los nombres de plato quedan en blanco/negro. */
.new__header {
  font-size: 1.3rem; /* al tamaño de los nombres de plato */
  font-weight: bold;
  text-align: center;
  text-transform: uppercase;
  color: var(--color-acento-carta, inherit);
}
.platos__familia {
  margin: 1.5rem 0; /* aire entre familias (first-child lo anula tras el hero) */
}
/* Imagen de familia (estándar = Hippie Chippy): redondeo COMPLETO con el
   radio de flota + sombra; alto por token --img-familia. (El Ancla iba con
   solo las esquinas superiores redondeadas.) */
.platos__familia-img {
  width: 100%;
  height: var(--img-familia, 8rem);
  margin-bottom: 0.8rem;
  border-radius: var(--border-radius);
  box-shadow: var(--sombra);
  object-fit: cover;
}
/* Tarjeta del bloque de platos (estándar = El Ancla): el título de familia
   queda FUERA, sobre el fondo de la app; el cristal (liquid-glass.css, vía la
   clase .cont-platos que añade cartaMaster) envuelve solo los platos. Aquí va
   la métrica de la tarjeta; el efecto cristal vive en liquid-glass.css. */
.cont-platos {
  border-radius: var(--border-radius);
  padding: 1rem;
  box-shadow: var(--sombra);
}

/* Separación entre platos: 0.3rem sobre el aire natural de la tipografía
   (decisión Manu jul 2026, tras eliminar el aire fantasma de las cabeceras
   vacías). El token queda como palanca por app. El .container no suma nada. */
.nuevo__producto {
  margin-top: var(--gap-platos, 0.3rem);
}
/* Rejilla nombre/precios de cada plato (estándar Ancla: columnas 1fr auto). */
.container {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  line-height: 1rem;
  gap: 1rem;
  margin-bottom: 0;
}
.nuevo__producto:last-of-type {
  margin-bottom: 1rem;
}

/* Cabeceras de columnas de precios (copa/botella o textos): cada imagen ocupa
   EXACTAMENTE el ancho de su columna (3.5rem, como los spans de precio) con el
   mismo gap → quedan CENTRADAS sobre su columna. Aire inferior hacia el primer
   producto. Alturas escalonadas (copa < botella…) como identidad de flota. */
.cabeceras__columnas {
  display: flex;
  justify-content: end;
  align-items: baseline;
  gap: 0.5rem; /* mismo gap que .cont__prices */
  margin-bottom: 0.6rem;
}
/* cartaMaster crea las dos filas de cabeceras (iconos y textos) en TODOS los
   productos aunque vayan vacías; sin esto, cada una suma su margin-bottom y
   mete ~1.2rem de aire fantasma entre platos. */
.cabeceras__columnas:empty {
  display: none;
}
.cabecera__columnas-imagen {
  width: 3.5rem; /* ancho de columna: centrado exacto sobre los precios */
  margin: 0;
  object-fit: contain;
}
.cabecera__columnas-imagen:nth-child(1) { height: 1.5rem; }
.cabecera__columnas-imagen:nth-child(2) { height: 1.8rem; }
.cabecera__columnas-imagen:nth-child(3) { height: 2.1rem; }
.cabecera__columnas-imagen:nth-child(4) { height: 2.4rem; }
.cabecera__columnas-imagen:nth-child(5) { height: 2.7rem; }
.cabecera__columna-texto {
  width: 3.5rem;
  text-align: center;
  /* Convención de cabeceras portada desde la app de imprimir (jul 2026): más
     pequeña, en negrita y partiendo por sílabas. Sin esto, las cabeceras de una
     sola palabra larga se DESBORDAN de su columna y se meten sobre la vecina
     ("Bocadillo" pedía 66px en la caja de 56). La negrita compensa el tamaño:
     se leen igual siendo más compactas.
     El -4px está MEDIDO en Chromium contra los 36 textos de cabecera reales de
     la flota: es el primer valor con el que el peor caso ("Bocadillo") entra en
     UNA línea dentro de los 3.5rem; con -3px todavía se parte en dos. Desborde
     máximo 0.0px. La única que sigue en dos líneas es "King Prawns", que parte
     por el espacio y es lo correcto.
     OJO: aquí el tamaño va DIRECTO en el span, al revés que en la app de
     imprimir, donde tiene que ir en un hijo porque allí la caja mide 6ch y `ch`
     se resuelve contra la fuente del propio elemento (encogerla descuadraría la
     rejilla). Aquí la caja es 3.5rem, ajena al font-size, así que no hace falta.
     OJO 2: hyphens silabea con el `lang` del <html>, uno fijo por app, así que
     una carta en inglés de una app en español se silabea con reglas españolas. */
  font-size: calc(1em - 4px);
  font-weight: 700;
  hyphens: auto;
  overflow-wrap: break-word;
}

/* Estrellas de la página de reseñas (estándar de flota, referencia Hippie
   Chippy): ARRIBA del logo, centradas, alto fijo. La posición en el HTML
   también es contrato: <img .reviews-stars> antes del hero, mismo contenedor. */
.reviews-stars {
  display: block;
  height: 4rem;
  width: auto;
  margin: 4rem auto 1rem;
}
/* Logo de la barra de header (allergens/reviews): MISMA ALTURA en toda la
   flota. Se dimensiona por ALTO (cada logo tiene su proporción; dimensionando
   por ancho, cada app acababa con una altura distinta).
   ESTÁNDAR (Manu, jul 2026): el header lleva /images/favicon.webp — la marca
   CUADRADA y TRANSPARENTE de la app (misma imagen que el .movimiento del
   index), no el logo panorámico ni los PNG de favicones/ (esos llevan fondo
   blanco horneado para iOS). */
.allergens__header-logo-img,
.reviews__header-logo-img {
  height: 3.5rem;
  width: auto;
  padding: 0.2rem;
}

/* ----------------------------------------------------------------------------
   ASIDE — aspecto compartido: degradado + sombra + flex column + espaciado.
   NO toca position/width/height: cada app mantiene su layout (fixed/sticky…).
   ---------------------------------------------------------------------------- */
.aside {
  background: var(--color-degradado2);
  box-shadow: var(--sombra);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--aside-gap, 1.25rem); /* separación entre iconos */
  padding: var(--aside-padding, 5rem 0.4rem);
}

/* ----------------------------------------------------------------------------
   FAMILIAS DE LA CARTA — APARIENCIA (degradado + sombra + radio) + padding
   interno. El padding 0.4rem se promovió a la base (jul 2026) porque era idéntico
   en TODA la flota salvo Kíkere (título pegado al borde). NO toca margin/display
   propios de cada app (su estructura de plato es zona de identidad de layout).
   ---------------------------------------------------------------------------- */
.platos__familia {
  background: var(--color-degradado2);
  box-shadow: var(--sombra);
  border-radius: var(--border-radius);
  padding: 0.4rem; /* aire del título/productos dentro de la tarjeta (estándar El Ancla) */
}

/* ----------------------------------------------------------------------------
   ICONOS DE NAVEGACIÓN — sistema de MÁSCARA + currentColor (estándar de flota).
   UN solo juego de SVG en /images/; el COLOR por defecto es --color-icono-nav
   (tema-aware: blanco en apps oscuras, color de marca en claras), IGUAL en TODAS
   las páginas — incluidos los iconos del index junto al texto (convención de
   consistencia, Manu jul 2026). Un contexto que necesite otro color lo pone con
   su propio `background-color`. Uso en HTML: <span class="icono icono--home"></span>.
   Requiere que la app tenga los SVG en /images/ (los tiene toda la flota).
   ---------------------------------------------------------------------------- */
.icono {
  display: inline-block;
  width: 2rem;
  height: 2rem;
  vertical-align: middle;
  background-color: var(--color-icono-nav);
  -webkit-mask: var(--icono) center / contain no-repeat;
  mask: var(--icono) center / contain no-repeat;
}
.icono--home { --icono: url(/images/icon_home.svg); }
.icono--menu { --icono: url(/images/icon_menu.svg); }
.icono--drink { --icono: url(/images/icon_drink.svg); }
.icono--sugerencias { --icono: url(/images/icon_sugerencias.svg); }
.icono--desayuno { --icono: url(/images/icon_desayuno.svg); }
.icono--dessert { --icono: url(/images/icon_dessert.svg); }
.icono--allergen { --icono: url(/images/icon_allergen.svg); }
.icono--stars { --icono: url(/images/icon_stars.svg); }
.icono--map { --icono: url(/images/icon_map.svg); }
.icono--call { --icono: url(/images/icon_call.svg); }
.icono--cerrar { --icono: url(/images/cerrar.svg); }


/* ----------------------------------------------------------------------------
   MODALES — animación de aparición compartida: CRECER DESDE EL CENTRO + rebote.
   Estándar de flota (jul 2026). Cubre el modal de promo del index
   (.modal__index-img) y el modal de mapas (.maps-modal-content).
   OJO: el CIERRE al pulsar fuera de la imagen es JavaScript (modal.js de cada
   app) — la base solo aporta la animación CSS. Ver CONTRATO-TOKENS.md §gotchas.
   ---------------------------------------------------------------------------- */

/* Modal de promo del index: la imagen crece desde el centro (tras 3s, junto al
   fade del overlay). 'both' = pequeña/invisible durante el delay y fija al fin. */
@keyframes modalImgIn {
  from { transform: translate(-50%, -50%) scale(0); opacity: 0; }
  to   { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}
.modal__index-img {
  animation: modalImgIn var(--modal-crecer-dur) var(--ease-rebote) 3s both;
}

/* Modal de mapas: el contenido crece desde el centro con rebote al abrir
   (usa transición porque abre al pulsar el botón, con la clase .active). */
.maps-modal-content {
  transform: scale(0.5);
  transition:
    transform var(--modal-crecer-dur) var(--ease-rebote),
    opacity 0.3s ease-out;
}
.maps-modal.active .maps-modal-content {
  transform: scale(1);
}

/* ----------------------------------------------------------------------------
   FONDO INTERIOR (páginas que NO son el index) — estándar de flota.
   Un solo elemento a pantalla completa con DOS capas por CSS:
     1) --logo-fondo: imagen flotante, centrada, ancho 50dvw (alto auto → no deforma).
        Pon `none` para no mostrar logo (solo fondo).
     2) --fondo-interior: fondo que cubre la pantalla (cover). Fallback: /images/fondo.webp.
   HTML: <div class="logo__fondo"></div> (vacío). Logo distinto por página →
   sobrescribir --logo-fondo con un modificador (p. ej. .logo__fondo--bebidas).
   ---------------------------------------------------------------------------- */
.logo__fondo {
  /* Unidades de viewport (dvw/dvh), no %: el % de un fijo depende del bloque
     contenedor y en algunas páginas dejaba MARCO o desaparecía. dvw/dvh lo atan
     SIEMPRE al tamaño real de la pantalla, como el fondo del index. */
  width: 100dvw;
  height: 100dvh;
  position: fixed;
  top: 0;
  left: 0;
  /* ESCALA DE CAPAS DE LA FLOTA (regla 25 jul, tras el bug de Mely):
       fondo de página -3  <  watermark/logo de fondo -2  <  contenido -1
     El contenido de las cartas vive dentro de `.wrapper`, que usa z-index:-1.
     Un fondo a -1 EMPATA con él y, si va después en el DOM, GANA: lo tapa y se
     traga sus clics. Por eso aquí SIEMPRE -2 (o menos), nunca -1. */
  z-index: -2;
  /* Es decorativo: que no intercepte clics pase lo que pase con el orden del
     DOM. Cinturón y tirantes junto al z-index. */
  pointer-events: none;
  background-color: var(--fondo-negro, #000);
  background-image: var(--logo-fondo, none), var(--fondo-interior, url(/images/fondo.webp));
  background-repeat: no-repeat, no-repeat;
  background-position: center, center;
  /* Fondo: 100% 100% ESTIRA la imagen para llenar SIEMPRE ancho y alto (igual
     que el index), en vez de `cover` que recorta y dejaba marco en páginas
     largas/cortas. Capa 1 = logo flotante (50dvw), capa 2 = fondo estirado. */
  background-size: 50dvw auto, 100% 100%;
}

/* En pantallas grandes el logo flotante se reduce (a 50dvw se ve demasiado). */
@media (min-width: 64rem) {
  .logo__fondo {
    background-size: 20dvw auto, 100% 100%;
  }
}

/* ---- Imagen de cabecera (HERO) — MISMA en todas las páginas ----
   Un único componente para la imagen grande de arriba (portada y cabecera de
   cartas/alérgenos/reseñas), para que se vea IGUAL en toda la app.
   Por defecto es una FOTO: rellena el marco (cover), como las imágenes de familia.
   Si es un LOGO, el JS (pintarHero en app.js) le añade `--logo`: no se recorta
   (contain + aire) y el glass lo tiñe por las zonas transparentes. La detección
   es por el nombre del archivo ("logo" -> logo); se puede forzar con la clase. */
/* HERO ÚNICO DE FLOTA (referencia: index de Hippie Chippy, jul 2026).
   MISMO tamaño y posición en TODAS las páginas con imagen de cabecera:
   alto natural con tope de 10rem, 1rem de aire arriba, 2rem abajo (sin él
   la imagen se pegaba al contenido) y 1rem de aire lateral.
   --hero-top (token con fallback, patrón del aside): las páginas con header
   FIJO y el hero como primer hijo directo necesitan más despeje arriba —
   lo aporta la app por página (p. ej. .allergens { --hero-top: 4rem }),
   la base no lo impone. */
.hero-img {
  width: calc(100% - 2rem);
  /* Tope DURO por viewport: si el hero vive en un contenedor que se encoge a
     su contenido (div flex sin ancho), el % se resuelve contra el ancho
     NATURAL de la imagen y un logo panorámico desborda la pantalla (bug del
     logo 700x227 de Decades en reviews, jul 2026). El min() lo impide. */
  max-width: min(var(--ancho-contenido, 50rem), calc(100dvw - 2rem));
  height: auto;
  max-height: 10rem;
  margin: var(--hero-top, 1rem) auto 2rem;
  border-radius: var(--border-radius);
  box-shadow: var(--sombra);
  object-fit: cover;
}
/* Hero de las páginas de carta: va dentro de .main (que ya baja 3rem), le
   basta medio rem propio. */
.carta,
.drinks {
  --hero-top: 0.5rem;
}
/* La PRIMERA familia de la carta no suma su margen al del hero: el hueco
   hero→contenido queda en el estándar (2rem exactos). El margen ENTRE
   familias se conserva (petición Manu: antes se acumulaban ~4rem). */
.platos__familia:first-child {
  margin-top: 0;
}
/* Header del index: MISMA altura fija que los interiores (4rem), lleve
   botón de reservas, logo o solo banderas. */
.index__buttons {
  gap: var(--btn-index-sep, 1.5rem);
}
.index__buttons-btn {
  padding: var(--btn-index-padding, 0.6rem 0.4rem 0.6rem 1.2rem);
  gap: var(--btn-index-gap, 0.75rem);
}
.index__header {
  display: flex;
  align-items: center;
  height: var(--alto-header, 4rem);
}
/* En el index el CONTENEDOR (body.index) ya pone el rem de aire por lado. */
.index .hero-img {
  width: calc(100% - 1rem);
  margin-left: 1rem;
  margin-right: 0;
}
/* Hero de las cabeceras de carta/bebidas = MISMO ancho que el contenido (los
   pills de familia ocupan el 100% de .main). La base deja .hero-img en
   calc(100% - 2rem) → 2rem más estrecho; aquí se ensancha a 100% para que el
   hero case con las familias de abajo. (Regla de flota · RECETA §10.) */
.carta .hero-img,
.drinks .hero-img {
  width: 100%;
}
/* Logos del index: pegados al header (sin el despeje de páginas interiores).
   El aire inferior es el ESTÁNDAR general (2rem): mismas medidas en toda la
   flota — lo que cambia por app son colores y contenidos, no la métrica. */
.index .hero-img--logo {
  margin-top: 0;
}
.hero-img--logo {
  object-fit: contain;
  /* Sin padding superior (empujaba el logo hacia abajo). El margin-top NO se
     toca aquí: lo gobierna --hero-top (las páginas interiores con header fijo
     lo necesitan); el index sí lo anula en su regla de arriba. */
  padding: 0 1rem 1rem;
  /* Regla de flota: los LOGOS van limpios, flotando sobre el fondo — sin
     borde, sin sombra y sin fondo (anula lo que .hero-img pone para fotos). */
  border-radius: 0;
  box-shadow: none;
  background: none;
}
/* FEATURE OPT-IN "marco" (nació en Decades, jul 2026): logo enmarcado en
   tarjeta de cristal — tinte + blur directos sobre el <img> (no admite
   pseudo-elementos), radio y sombra de flota. La app que lo quiera añade
   la clase junto a --logo (se define después, así que gana); las demás
   conservan el logo limpio. Las medidas siguen siendo las de .hero-img.
   REGLA DE FLOTA (Manu, jul 2026): el marco es una decisión DE APP y va A
   JUEGO — si el logo del index lleva marco, los heros de las páginas de
   carta que sean logo lo llevan también (los que son FOTO, no: conservan su
   caja de .hero-img). En alérgenos y reviews el logo va SIEMPRE limpio
   (guardarraíl abajo). Cada app decide si sus cartas llevan logo o foto. */
.hero-img--marco {
  padding: 1rem;
  border-radius: var(--border-radius);
  box-shadow: var(--sombra);
  background-color: var(--lg-bg-color);
  -webkit-backdrop-filter: blur(var(--lg-blur, 0.25rem)) saturate(120%) brightness(1.15);
  backdrop-filter: blur(var(--lg-blur, 0.25rem)) saturate(120%) brightness(1.15);
}
/* Guardarraíl de la regla anterior: en las páginas de alérgenos y reviews el
   marco se NEUTRALIZA aunque la clase esté puesta — vuelve al logo limpio. */
.allergens .hero-img--marco,
.reviews .hero-img--marco {
  padding: 0 1rem 1rem;
  border-radius: 0;
  box-shadow: none;
  background: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* ----------------------------------------------------------------------------
   COLUMNAS DE PRECIOS EN MÓVIL (fix nacido en Lanis, jul 2026).
   Problema: con 3-4 columnas, el nombre del plato empujaba los precios y las
   columnas se desalineaban entre filas. Solución: dígitos tabulares (mismo
   ancho por cifra) y, cuando el producto tiene 4 precios (cartaMaster añade
   .precios-cuatro-columnas y .producto-cuatro-precios), columnas y cabeceras
   se encogen a ancho FIJO en pantallas pequeñas.
   ---------------------------------------------------------------------------- */
.cont__prices span {
  font-variant-numeric: tabular-nums;
}
/* Resaltar columnas pares cuando hay 4 precios (lectura en tabla) */
.precios-cuatro-columnas span:nth-child(even) {
  font-weight: bold;
}
@media (max-width: 60rem) {
  h4.producto-cuatro-precios {
    font-size: 1.175rem;
  }
  .precios-cuatro-columnas {
    gap: 0.3rem;
  }
  .precios-cuatro-columnas span {
    font-size: 0.975rem;
    width: 2.8rem;
  }
  /* Solo cabeceras de 4 columnas (:has cuenta los hijos) */
  .cabeceras__columnas:has(> :nth-child(4)) {
    gap: 0.3rem;
  }
  .cabeceras__columnas:has(> :nth-child(4)) > .cabecera__columna-texto {
    width: 2.8rem;
  }
  .cabeceras__columnas:has(> :nth-child(4)) > .cabecera__columnas-imagen {
    width: 2.8rem;
    height: auto;
  }
}
@media (max-width: 30rem) {
  h4.producto-cuatro-precios {
    font-size: 0.85rem;
  }
  .precios-cuatro-columnas {
    gap: 0.2rem;
  }
  /* El gap de las cabeceras tiene que bajar CON el de los precios: si los
     precios van a 0.2rem y las cabeceras se quedan en los 0.3rem del bloque de
     60rem, la diferencia se acumula hueco a hueco y con 4 columnas son 3 × 1.6px
     = 4.8px de desvío al final de la fila. Faltaba, y por eso el arreglo de los
     anchos dejaba todavía esos 4.8px. Mismo `:has` que arriba, por especificidad. */
  .cabeceras__columnas:has(> :nth-child(4)) {
    gap: 0.2rem;
  }
  .precios-cuatro-columnas span {
    font-size: 0.75rem;
    width: 2.3rem;
  }
  /* El `:has` NO es decorativo, es lo que hace que esto llegue a aplicarse.
     Estas reglas iban sin él y NUNCA ganaban: el bloque de 60rem de arriba
     estrecha las cabeceras con `.cabeceras__columnas:has(…) > .cabecera__…`,
     que es MÁS ESPECÍFICO que un `.cabecera__columna-texto` suelto, y las media
     queries no suman especificidad. Resultado: por debajo de 480px los precios
     bajaban a 2.3rem y las cabeceras se quedaban en los 2.8rem del bloque
     anterior → 32.8px de desvío entre una fila y otra (medido en Chromium).
     Con el mismo `:has` empatan en especificidad y gana esta por ir después.
     Y va acotado a 4 columnas A PROPÓSITO, igual que la regla de los precios:
     sin acotar, encogía también las cabeceras de las cartas de 2 y 3 columnas,
     cuyos precios NO encogen (no llevan .precios-cuatro-columnas) — que era el
     segundo desajuste escondido aquí. */
  .cabeceras__columnas:has(> :nth-child(4)) > .cabecera__columna-texto {
    width: 2.3rem;
    font-size: 0.75rem;
  }
  .cabeceras__columnas:has(> :nth-child(4)) > .cabecera__columnas-imagen {
    width: 2.3rem;
    height: auto;
    margin: 0;
    object-fit: contain;
  }
}

/* ----------------------------------------------------------------------------
   PRECIOS EN SU PROPIA LÍNEA EN MÓVIL, CON 3+ COLUMNAS (Manu, 3 ago 2026).
   Nació mirando INDIAN CURRY de Royal Kebab en un iPhone.
   ----------------------------------------------------------------------------
   PROBLEMA: `.container` es una rejilla `1fr auto`. Con 3 o 4 columnas de precio
   el lado "auto" se lleva ~200px y al nombre y la descripción no les queda casi
   nada: "43. POLLO TIKKA MASALA" caía en TRES líneas y la descripción bajaba en
   una columna de dos palabras. Encima, en el borde, el último precio se salía de
   la pantalla (se veía "€13" cortado).
   Hasta aquí la flota lo mitigaba ENCOGIENDO (las reglas de 4 columnas de
   arriba: hasta 0.75rem de letra y 2.3rem de caja). Eso deja los precios casi
   ilegibles y el texto sigue estrecho: trata el síntoma, no la causa.

   SOLUCIÓN: en móvil, cuando el plato tiene 3+ precios, la rejilla pasa a UNA
   sola columna → nombre y descripción a ancho COMPLETO, y los precios bajan a su
   propia línea debajo. Como `.cont__prices` ya va con `justify-content: end` y
   con los mismos anchos y gap que `.cabeceras__columnas`, cada precio sigue
   cayendo EXACTAMENTE bajo su cabecera (Chicken / Lamb / Prawn): se conserva la
   lectura en tabla, que es lo que hace comparable una carta de este tipo.

   Se detecta con `:has` sobre el 3er precio, así que NO toca el JS ni la data, y
   los platos de 1 o 2 precios se quedan exactamente como estaban. Al liberar el
   ancho se ANULA además el encogido de 4 columnas: los precios recuperan tamaño
   legible, y las cabeceras vuelven a 3.5rem para no desalinearse de ellos.
   ---------------------------------------------------------------------------- */
@media (max-width: 37.5rem) {
  .container:has(.cont__prices > span:nth-child(3)) {
    grid-template-columns: 1fr;
    align-items: start;
    gap: 0.35rem;
  }
  /* Los precios ya no compiten por el ancho → caja y tamaño normales. */
  .container:has(.cont__prices > span:nth-child(3)) .cont__prices {
    gap: 0.5rem;
  }
  .container:has(.cont__prices > span:nth-child(3)) .cont__prices span {
    width: 3.5rem;
    font-size: 1.1rem;
  }
  /* Las cabeceras acompañan: mismo ancho y gap que los precios, o la columna
     dejaría de caer bajo su título. */
  .cabeceras__columnas:has(> :nth-child(3)) {
    gap: 0.5rem;
  }
  .cabeceras__columnas:has(> :nth-child(3)) > .cabecera__columna-texto,
  .cabeceras__columnas:has(> :nth-child(3)) > .cabecera__columnas-imagen {
    width: 3.5rem;
  }
  /* El nombre recupera su tamaño: ya no hay que encogerlo para hacer sitio. */
  h4.producto-cuatro-precios {
    font-size: inherit;
  }
}

/* ----------------------------------------------------------------------------
   ANIMACIONES FADE-IN AL SCROLL (estándar de flota = El Ancla, jul 2026).
   Las clases las gestiona js/scrollAnimations.js (IntersectionObserver añade
   .visible). Sustituyen a la librería externa AOS (una dependencia menos).
   ---------------------------------------------------------------------------- */
.fade-in-element {
  opacity: 0;
  transform: translateY(1.875rem);
  transition: opacity 1.3s ease-out, transform 1.3s ease-out;
}
.fade-in-element.visible {
  opacity: 1;
  transform: translateY(0);
}
.fade-in-left {
  opacity: 0;
  transform: translateX(-3.125rem);
  transition: opacity 1.3s ease-out, transform 1.3s ease-out;
}
.fade-in-left.visible {
  opacity: 1;
  transform: translateX(0);
}
.fade-in-right {
  opacity: 0;
  transform: translateX(3.125rem);
  transition: opacity 1.3s ease-out, transform 1.3s ease-out;
}
.fade-in-right.visible {
  opacity: 1;
  transform: translateX(0);
}
.fade-in-scale {
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 1.3s ease-out, transform 1.3s ease-out;
}
.fade-in-scale.visible {
  opacity: 1;
  transform: scale(1);
}

/* Títulos de cada alérgeno en la lista (estándar = El Ancla): 1.2rem.
   La descripción queda en 1rem (ya uniforme). El color lo pone cada app. */
.allergen__allergen h3 {
  font-size: 1.2rem;
}

/* Tarjeta de la lista de alérgenos (.contenido, la crea allergen.js):
   misma receta que .allergens__info — cristal y borde, del glass. */
.contenido {
  width: 90dvw;
  max-width: 50rem;
  border-radius: var(--border-radius);
  box-shadow: var(--sombra);
}

/* ----------------------------------------------------------------------------
   FOOTER (estándar de flota = estilo Hippie Chippy, jul 2026):
   nombre del negocio en MAYÚSCULAS (por CSS, no por cómo esté la data) y
   acentos de color en nombre/créditos/email vía token --color-footer-acento
   (cada app pone el suyo: HC secundario azul, El Ancla el amarillo de título).
   ---------------------------------------------------------------------------- */
.footer {
  display: flex;
  flex-direction: column;
  text-align: center;
  padding: 0.5rem;
  padding-bottom: 3rem;
}
.footer__div {
  font-weight: bold;
  margin: 1rem;
  text-align: center;
}
.footer__div h3 {
  text-transform: uppercase;
  color: var(--color-footer-acento, inherit);
}
.footer__creditos {
  font-size: 0.8rem;
  text-align: center;
  color: var(--color-footer-acento, inherit);
}
.footer__creditos span {
  font-size: 0.8rem;
}
.footer .email {
  text-align: center;
  padding: 1rem;
  color: var(--color-footer-acento, inherit);
}
.footer .restaurant123 {
  color: var(--color-footer-acento, inherit);
  font-size: 0.8rem;
}

/* PASTILLAS DE OPCIONES del formulario de reservas (zona, primera visita).
   Como en la app de reservas emmax: radios ocultos + labels como botones. La
   opción marcada se rellena con el color de marca. Usa los tokens del tema, así
   se ve bien en oscuro y en claro. */
.form .opciones {
  display: flex;
  /* fila explícita: el `.form div { flex-direction: column }` del tema apila
     por defecto, y sin esto las pastillas saldrían una debajo de otra. */
  flex-direction: row;
  gap: 0.5rem;
}
.form .opciones input[type="radio"] {
  /* Oculto pero accesible (el foco se ve en la píldora). */
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.form .opciones__pildora {
  flex: 1;
  text-align: center;
  margin: 0; /* anula el margin de .form label para alinear las pastillas */
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--color-form-borde);
  border-radius: 50px;
  font-size: 0.95rem;
  cursor: pointer;
  transition:
    background 0.2s ease,
    color 0.2s ease,
    border-color 0.2s ease;
}
/* Hover de las pastillas SIN seleccionar ("las suaves"): leve tinte de marca +
   el borde se resalta, para que se noten pulsables antes de elegirlas. */
.form .opciones input[type="radio"]:not(:checked) + .opciones__pildora:hover {
  border-color: var(--color-primario);
  background: color-mix(in srgb, var(--color-primario) 10%, transparent);
}
.form .opciones input[type="radio"]:checked + .opciones__pildora {
  background: var(--color-primario);
  border-color: var(--color-primario);
  color: var(--color-texto-invertido, #fff);
}
.form .opciones input[type="radio"]:focus-visible + .opciones__pildora {
  box-shadow: 0 0 0 3px var(--color-form-borde-focus);
}

/* ENTRADA SUAVE de los avisos del formulario (día cerrado, grupo grande,
   reserva inminente, "sujeto a disponibilidad"): al pasar de oculto a visible,
   la animación se dispara sola (los elementos con display:none la reejecutan al
   volver al render), así aparecen con un fundido + leve deslizamiento en vez de
   un salto seco. */
.nota-reserva {
  animation: nota-aparece 0.28s ease;
}
@keyframes nota-aparece {
  from {
    opacity: 0;
    transform: translateY(-0.25rem);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .nota-reserva {
    animation: none;
  }
}
