/* ============================================================================
   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.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   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: 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;
  }
}

/* ----------------------------------------------------------------------------
   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;
  }
}
