/* Inter autoalojada (vendor/fonts): fuente variable 300-700, subconjunto latin.
   Sin petición a fonts.googleapis.com en runtime (rendimiento y RGPD). */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url('vendor/fonts/inter-latin.woff2') format('woff2');
}

:root {
    --bg: #F7F0E6;
    --panel: #F1E8DA;           /* superficie cálida en claro (burbujas del bot, hovers de lista) */
    --sidebar-bg: #2F3B4A;
    --sidebar-hover: #3A4655;
    --sidebar-active: #3E4B5C;
    --sidebar-text: #A9B6C4;
    --sidebar-text-active: #F7F0E6;
    /* Tonos SOLO para superficies marino (sidebar/topbar): los grises azulados de la
       paleta no alcanzan AA sobre crema, pero sobre marino rinden ~5:1. */
    --sidebar-muted: #9FAEBC;
    --sidebar-line: rgba(247, 240, 230, 0.14);
    --text: #2F3B4A;
    /* Texto secundario: topo CÁLIDO (el gris azulado de la paleta propuesta daba 2.6:1
       sobre crema y se descartó para texto). AA con margen: #F7F0E6 → 5.1:1, #fff → 5.7:1. */
    --text-secondary: #6B6459;
    --border: #DCCFC0;
    --card-bg: #ffffff;
    /* --accent-rgb es la fuente única del marino de marca; --accent y las
       translucencias (sombras, hovers, foco) se derivan de él para que un
       rebrand toque un solo valor. */
    --accent-rgb: 47, 59, 74;
    --accent: rgb(var(--accent-rgb));
    --accent-hover: #3D4C60;
    /* Marino SUAVIZADO para los botones (feedback 07-10: el grafite puro resultaba
       duro como fondo de botón); el --accent queda para texto, titulares y superficies. */
    --accent-soft: #46566A;
    --accent-dark: #232D3A;
    --on-accent: #F7F0E6;       /* texto/icono sobre fondo de marca (crema, no blanco puro: calidez) */
    --green: #4A7551;
    --green-hover: #3E6345;
    --danger: #B0483A;
    --danger-hover: #93392C;
    --gold: #B58D53;            /* latón: bordes/acentos, nunca texto pequeño sobre claro */
    --gold-text: #8A6535;       /* latón oscurecido para TEXTO (AA ~4.8:1) y fondo del .btn-gold */
    --gold-hover: #74542C;      /* hover del CTA latón */
    --success: #2F7A50;         /* verde de confirmación (antes solo existía como fallback) */
    --info: #4A7096;            /* azul acero armonizado con el marino (estados informativos) */
    --shadow-sm: 0 1px 3px rgba(var(--accent-rgb), 0.05);
    --shadow-md: 0 4px 12px rgba(var(--accent-rgb), 0.08);
    --shadow-lg: 0 8px 30px rgba(var(--accent-rgb), 0.12);
    --radius: 10px;
    --radius-lg: 16px;
    --radius-sm: 6px;
    --radius-pill: 999px;
    /* Escala tipográfica (usar en código nuevo; unifica ~30 tamaños sueltos) */
    --fs-xs: 0.75rem;
    --fs-sm: 0.85rem;
    --fs-base: 0.9rem;
    --fs-md: 1rem;
    --fs-lg: 1.25rem;
    --fs-xl: 1.5rem;
    --fs-input: 16px;           /* anti-zoom iOS: no convertir a rem */
    /* Escala de espaciado 4/8px (usar en código nuevo) */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-6: 1.5rem;
    --space-8: 2rem;
}

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    font-family: 'Inter', sans-serif;
    background-color: var(--bg);
    color: var(--text);
    line-height: 1.6;
    /* dvh = alto realmente visible en móvil (la barra dinámica del navegador
       sobreestima 100vh); el vh queda como fallback para navegadores antiguos. */
    height: 100vh;
    height: 100dvh;
    display: flex;
}

/* ── Auth View ── */

#auth-view {
    display: flex;
    flex-direction: column;
    align-items: center;
    /* `safe center`: centra cuando cabe, pero al desbordar (móvil apaisado o teclado
       abierto) alinea arriba en vez de recortar la parte superior del formulario. */
    justify-content: safe center;
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    overflow-y: auto;
    padding: 1.5rem 0;
    background: #2F3B4A;
}

.auth-container {
    background: var(--card-bg);
    padding: 3rem 2.5rem;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    text-align: center;
    width: 380px;
}

.auth-brand {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.85rem;
    margin-bottom: 0.5rem;
}

/* Medallón de marca: la batidora del logo (SVG inline en index.html) en trazo
   crema/latón sobre círculo marino con aro dorado; sin fichero de imagen. */
.auth-logo {
    width: 64px;
    height: 64px;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--accent);
    border: 2px solid var(--gold);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    font-family: Georgia, 'Times New Roman', serif;
    font-size: 1.8rem;
    line-height: 1;
    color: #f3e9da;
    user-select: none;
}

.auth-container h2 {
    margin-bottom: 0;
    font-size: 2.1rem;
    font-weight: 700;
    letter-spacing: 0.5px;
    color: var(--text);
}

.auth-subtitle {
    color: var(--text-secondary);
    font-size: 0.9rem;
    margin-bottom: 1.2rem;
}

/* Aviso de Bloq Mayús bajo los campos de contraseña de auth (lo inserta auth.js). */
.capslock-aviso {
    margin: -0.6rem 0 0.8rem;
    font-size: 0.8rem;
    color: var(--gold-text);
}

/* Título del panel de activación por código (OTP). */
.auth-otp-title {
    text-align: center;
    color: var(--text);
    font-size: 1.25rem;
    margin: 0 0 0.4rem;
}

/* Código OTP grande y centrado: se comprueba de un vistazo contra el email (antes era
   un input normal de 16px alineado a la izquierda, difícil de verificar a contraluz). */
#otp-code {
    font-size: 1.5rem;
    text-align: center;
    letter-spacing: 0.35em;
    font-variant-numeric: tabular-nums;
}

/* El placeholder «000000» es solo una guía de formato (la instrucción real está en el
   rótulo y el subtítulo), así que va en un tono claro para no competir con el código que
   teclea el usuario —a 1,5rem el gris de placeholder general quedaba demasiado fuerte. */
#otp-code::placeholder {
    color: #c9bcae;
}

/* Código incorrecto: borde rojo + sacudida del campo (el estado se retira al reescribir). */
#otp-code.otp-error {
    border-color: var(--danger);
    box-shadow: 0 0 0 1px var(--danger);
}

.otp-shake,
.modal-shake {
    animation: otp-shake 0.4s ease;
}

@keyframes otp-shake {
    0%, 100% { transform: translateX(0); }
    20%, 60% { transform: translateX(-6px); }
    40%, 80% { transform: translateX(6px); }
}

.auth-tabs {
    display: flex;
    gap: 0;
    margin-bottom: 1.5rem;
    border-bottom: 2px solid var(--border);
}

.auth-tab {
    flex: 1;
    padding: 0.6rem 1rem;
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    cursor: pointer;
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--text-secondary);
    /* Propiedades explícitas (no `all`): evita vigilar propiedades de layout. */
    transition: color 0.2s, border-color 0.2s, background-color 0.2s;
}

.auth-tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

.auth-tab:hover {
    color: var(--accent);
}

.auth-form-panel {
    text-align: left;
}

.auth-link {
    background: none;
    border: none;
    /* Área de toque perceptible (WCAG 2.5.8 ≥24px) manteniendo el aspecto de enlace. */
    padding: 0.4rem 0.6rem;
    color: var(--accent);
    font-size: 0.85rem;
    cursor: pointer;
    align-self: center;
}

.auth-link:hover {
    text-decoration: underline;
}

/* Spinner de botón (lo añade withDisabled): feedback "en curso" en cualquier botón. */
.btn-spinner {
    display: inline-block;
    width: 0.9em;
    height: 0.9em;
    margin-left: 0.5em;
    vertical-align: -0.12em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: btn-spin 0.6s linear infinite;
}

@keyframes btn-spin {
    to { transform: rotate(360deg); }
}

#auth-view form {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

/* ── Splash de bienvenida (cubre la carga inicial tras iniciar sesión) ── */

#splash-view {
    position: fixed;
    inset: 0;
    /* Por encima del aviso de cookies (1300) y de los modales (1000-1100), por
       debajo de los toasts (2000); un aviso de error durante la carga queda tras
       el splash pero este no captura clics y se desvanece solo. */
    z-index: 1500;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(160deg, #232D3A 0%, #2F3B4A 100%);
    color: var(--on-accent);
    opacity: 1;
    transition: opacity 0.5s ease;
    /* Solo cubre visualmente la carga; no captura clics (el contenido se está
       poblando debajo y no debe quedar bloqueado tras el fundido). */
    pointer-events: none;
}

#splash-view.fade-out {
    opacity: 0;
}

.splash-content {
    text-align: center;
    padding: 2rem;
    animation: splashIn 0.5s ease;
}

.splash-content h1 {
    font-size: 2.6rem;
    font-weight: 700;
    margin-bottom: 0.6rem;
    line-height: 1.1;
    /* «Hola, {nombre}»: un nombre largo sin espacios debe partir, no desbordar. */
    overflow-wrap: anywhere;
}

.splash-tagline {
    font-size: 1.1rem;
    color: rgba(255, 255, 255, 0.8);
    margin-bottom: 2rem;
}

.splash-spinner {
    width: 36px;
    height: 36px;
    margin: 0 auto;
    border: 3px solid rgba(255, 255, 255, 0.25);
    border-top-color: var(--on-accent);
    border-radius: 50%;
    animation: splashSpin 0.8s linear infinite;
}

@keyframes splashIn {
    from { opacity: 0; transform: translateY(12px); }
    to { opacity: 1; transform: translateY(0); }
}

@keyframes splashSpin {
    to { transform: rotate(360deg); }
}

/* ── Sidebar ── */

#app-view {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
}

/* Cuerpo en fila (sidebar + contenido) bajo los banners superiores. min-height:0 deja
   que sidebar y main gestionen su propio scroll dentro de la altura disponible. */
.app-shell {
    display: flex;
    flex: 1;
    min-height: 0;
    width: 100%;
}

.sidebar {
    width: 240px;
    min-width: 240px;
    background: var(--sidebar-bg);
    color: var(--sidebar-text);
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow-y: auto;
}

.sidebar-brand {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 1.5rem 1.25rem;
    border-bottom: 1px solid var(--sidebar-line);
    font-weight: 700;
    font-size: 1.05rem;
    color: #F3E9DA;
}

/* El nombre del negocio (hasta 60 chars) no debe desbordar la marca del menú/topbar:
   sin esto, un nombre largo sin espacios rompía el ancho del sidebar. */
.brand-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.brand-icon {
    width: 36px;
    height: 36px;
    background: var(--accent);
    color: #f3e9da;
    border: 1.5px solid var(--gold);
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: Georgia, 'Times New Roman', serif;
    font-size: 1.15rem;
    line-height: 1;
    flex-shrink: 0;
    user-select: none;
}

/* La batidora del logo (SVG inline) dentro del medallón de auth y de la teja del menú. */
.brand-icon svg,
.auth-logo svg {
    width: 72%;
    height: 72%;
    display: block;
}

.sidebar-nav {
    display: flex;
    flex-direction: column;
    padding: 0.75rem;
    flex: 1;
    gap: 2px;
}

.nav-section-label {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: var(--sidebar-muted);
    padding: 1rem 0.75rem 0.4rem;
    font-weight: 600;
}

nav button {
    background: transparent;
    color: var(--sidebar-text);
    border: none;
    padding: 0.6rem 0.75rem;
    cursor: pointer;
    font-size: 0.88rem;
    text-align: left;
    border-radius: 8px;
    transition: background-color 0.15s ease, color 0.15s ease;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

nav button svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    opacity: 0.6;
}

nav button:hover {
    background: var(--sidebar-hover);
    color: var(--sidebar-text-active);
}

nav button:hover svg {
    opacity: 1;
}

nav button.active {
    background: var(--sidebar-active);
    color: var(--on-accent);
    /* Marcador latón RECTO (feedback 07-10): borde real con las esquinas izquierdas
       a 0 — el inset anterior seguía el redondeo del botón y se curvaba. El padding
       compensa los 3px para que el texto no se desplace respecto al resto de ítems. */
    border-left: 3px solid var(--gold);
    border-radius: 0 8px 8px 0;
    padding-left: calc(0.75rem - 3px);
}

nav button.active svg {
    opacity: 1;
}

/* Bloque inferior fijo del menú: Ajustes, Salir y el texto de soporte. */
.sidebar-footer {
    margin-top: auto;
    border-top: 1px solid var(--sidebar-line);
    display: flex;
    flex-direction: column;
}

#btn-ajustes,
#logout-btn {
    color: var(--sidebar-text);
    border-radius: 0;
    padding: 0.85rem 0.75rem;
}

#logout-btn:hover {
    background: rgba(176, 72, 58, 0.3);
    /* Teja aclarada: el --danger puro sobre marino no llega a AA. */
    color: #FFB3A3;
}

.sidebar-support {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.85rem 0.9rem 1rem;
    font-size: 0.72rem;
    line-height: 1.4;
    color: var(--sidebar-muted);
    border-top: 1px solid var(--sidebar-line);
}

/* Subrayado en reposo: sin él, los enlaces embebidos en texto parecían solo
   texto coloreado hasta pasar el ratón (affordance débil). */
.sidebar-support a {
    /* Latón claro: enlaces legibles sobre marino (~6.6:1). */
    color: #D9C29A;
    text-decoration: underline;
    word-break: break-all;
}

/* ── Main Content ── */

.main-content {
    flex: 1;
    /* El padding inferior extra evita que el botón flotante del chat tape la
       última fila de las tablas o el eje X del último gráfico. */
    padding: 2rem 2.5rem 6.5rem;
    overflow-y: auto;
    background: var(--bg);
}

.page-header {
    margin-bottom: 1.5rem;
}

.page-header h2 {
    /* clamp: escala fluida entre el 1.35rem de móvil y el 1.6rem de escritorio
       (antes saltaba de golpe en el breakpoint). */
    font-size: clamp(1.35rem, 1.15rem + 0.6vw, 1.6rem);
    font-weight: 700;
    color: var(--text);
    margin-bottom: 0.2rem;
}

.page-desc {
    color: var(--text-secondary);
    font-size: 0.9rem;
}

h2 {
    margin-bottom: 0;
}

.tab-content {
    display: none;
    animation: fadeIn 0.25s ease;
}

.tab-content.active {
    display: block;
}

/* Solo opacity (sin transform): componer translate+opacity durante 250ms justo
   cuando Chart.js instancia 7 canvas producía tirones al entrar en Inicio. */
@keyframes fadeIn {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

/* ── Forms ── */

label {
    display: block;
    font-weight: 500;
    margin-bottom: 0.35rem;
    font-size: 0.85rem;
    color: var(--text-secondary);
}

input,
select,
textarea {
    width: 100%;
    padding: 0.55rem 0.75rem;
    border: 1px solid var(--border);
    background: #fff;
    color: var(--text);
    margin-bottom: 1rem;
    border-radius: var(--radius);
    transition: border-color 0.2s, box-shadow 0.2s;
    font-family: inherit;
    font-size: 0.9rem;
}

textarea {
    resize: vertical;
}

input[type="range"] {
    -webkit-appearance: none;
    appearance: none;
    height: 6px;
    background: var(--border);
    border-radius: 3px;
    border: none;
    padding: 0;
    cursor: pointer;
}

input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 20px;
    height: 20px;
    background: var(--accent);
    border-radius: 50%;
    cursor: pointer;
}

input[type="range"]::-moz-range-thumb {
    width: 20px;
    height: 20px;
    background: var(--accent);
    border-radius: 50%;
    border: none;
    cursor: pointer;
}

/* Placeholders legibles: el gris por defecto del navegador quedaba por debajo de AA
   y varios campos usan el placeholder como única pista (OTP, precios de ejemplo). */
input::placeholder,
textarea::placeholder {
    color: #6f6459;
    opacity: 1;
}

input:focus,
select:focus,
textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.12);
}

/* Con teclado, además del borde+sombra, un anillo explícito (WCAG 2.4.7): la sombra
   suave al 12% pasa desapercibida al tabular deprisa por un formulario. */
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

/* Texto solo para lectores de pantalla (visualmente oculto). */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Enlace «Saltar al contenido»: invisible hasta recibir foco con teclado. */
.skip-link {
    position: fixed;
    top: 8px;
    left: 8px;
    z-index: 3000;
    background: var(--accent);
    color: var(--on-accent);
    padding: 0.6rem 1rem;
    border-radius: var(--radius);
    text-decoration: none;
    transform: translateY(-250%);
}

.skip-link:focus {
    transform: translateY(0);
}

/* Columnas numéricas de tablas: a la derecha y con cifras tabulares, para comparar
   importes de un vistazo (en el modo tarjeta móvil el td ya va alineado a la derecha,
   así que la regla no interfiere). */
td.td-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Indicador de foco para navegación por teclado: los inputs ya cambian borde y
   sombra al enfocar, pero botones y enlaces no tenían ninguna señal visible. */
button:focus-visible,
a:focus-visible,
[role="button"]:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Campo obligatorio sin rellenar al intentar guardar: se limpia al editarlo. */
.field-error {
    border-color: var(--danger) !important;
    box-shadow: 0 0 0 3px rgba(176, 72, 58, 0.15) !important;
}

/* Asterisco de campo obligatorio. */
.req {
    color: var(--danger);
    margin-left: 2px;
}

.form-row {
    display: flex;
    gap: 1rem;
}

.form-row>div {
    flex: 1;
}

/* ── Mostrar/ocultar contraseña ── */

/* El wrap asume el margen inferior del input para que el "ojo" quede centrado
   respecto a la caja visible del campo, no a caja+margen. */
.pass-wrap {
    position: relative;
    margin-bottom: 1rem;
}

.pass-wrap input {
    margin-bottom: 0;
    padding-right: 2.6rem;
}

.pass-toggle {
    position: absolute;
    top: 50%;
    right: 0.35rem;
    transform: translateY(-50%);
    width: auto;
    min-height: 0;
    padding: 0.3rem;
    background: none;
    border: none;
    color: var(--text-secondary);
    display: flex;
    align-items: center;
}

.pass-toggle:hover {
    background: none;
    color: var(--text);
}

/* El ojo se centra con translateY(-50%). La regla global `button:active { scale(.97) }`
   lo pisaba: al pulsar perdía el centrado y saltaba arriba/abajo, y en táctil el botón
   se movía bajo el dedo, de modo que el clic a veces no llegaba a alternar la contraseña.
   Conservar el centrado en :active lo deja estático y fiable. */
.pass-toggle:active { transform: translateY(-50%); }

/* ── Combobox (lista cerrada con filtrado al escribir) ── */

/* Como .pass-wrap: el wrap asume el margen inferior del input para que la lista
   desplegada quede pegada a la caja visible del campo. */
.combobox {
    position: relative;
    margin-bottom: 1rem;
}

.combobox input {
    margin-bottom: 0;
}

.combobox-list {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 60;
    margin: 0;
    padding: 0.3rem;
    list-style: none;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    max-height: 220px;
    overflow-y: auto;
}

.combobox-list li {
    padding: 0.45rem 0.65rem;
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.9rem;
}

.combobox-list li[role="option"]:not([aria-disabled="true"]):hover,
.combobox-list li.combobox-activa {
    background: var(--panel);
}

/* Opción listada pero no elegible (p. ej. receta «⚠ rota» en el pedido): informa de
   que existe sin dejar seleccionarla, como la <option disabled> a la que sustituye. */
.combobox-list li[role="option"][aria-disabled="true"] {
    color: var(--text-secondary);
    cursor: not-allowed;
}

.combobox-list .combobox-vacio {
    color: var(--text-secondary);
    cursor: default;
}

/* ── Buttons ── */

button {
    /* Marino PLENO de marca (07-10): --accent-soft de base dejaba el primario
       grisáceo, como deshabilitado; el hover (más claro) ilumina sobre este fondo. */
    background: var(--accent);
    color: var(--on-accent);
    border: none;
    padding: 0.6rem 1.2rem;
    cursor: pointer;
    font-size: 0.9rem;
    font-weight: 500;
    border-radius: var(--radius);
    /* Incluye transform: el feedback táctil usa :active { scale }. */
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease,
        box-shadow 0.15s ease, opacity 0.15s ease, transform 0.15s ease;
}

button:hover {
    background: var(--accent-hover);
}

/* Deshabilitado SIN atenuar con opacity: sobre botones de color, el texto blanco al
   40% quedaba a ~1,5:1 de contraste (ilegible al sol del obrador). Un gris sólido
   comunica "no disponible" manteniendo el rótulo legible. */
button:disabled {
    background: #c9bcae;
    color: var(--on-accent);
    cursor: not-allowed;
}

.btn-secondary:disabled,
.btn-danger-ghost:disabled,
.btn-cancel:disabled {
    background: transparent;
    color: #a89a8c;
    border-color: #c9bcae;
}

.pass-toggle:disabled {
    background: none;
}

.btn-secondary {
    background: transparent;
    color: var(--accent);
    /* Sólido, no discontinuo: el dashed leía como «zona de soltar», no como acción. */
    border: 1px solid var(--accent);
    width: 100%;
    margin-top: 0.5rem;
}

.btn-secondary:hover {
    background: rgba(var(--accent-rgb), 0.05);
}

.btn-success {
    background: var(--green);
}

.btn-success:hover {
    background: var(--green-hover);
}

/* CTA de marca (Guardar receta): el latón entra en la capa de acción. El fondo usa
   el latón de texto (#8A6535, AA con crema); --gold puro se queda corto de contraste. */
.btn-gold {
    background: var(--gold-text);
}

.btn-gold:hover {
    background: var(--gold-hover);
}

.btn-danger {
    background: var(--danger);
}

.btn-danger:hover {
    background: var(--danger-hover);
}

/* Variante atenuada para el "Eliminar" de las filas de tabla: la acción
   destructiva no debe ser la más llamativa de la fila. Se rellena al hover. */
.btn-danger-ghost {
    background: transparent;
    color: var(--danger);
    border: 1px solid var(--danger);
}

.btn-danger-ghost:hover {
    background: var(--danger);
    color: var(--on-accent);
}

.btn-small {
    padding: 0.35rem 0.7rem;
    font-size: 0.8rem;
}

/* ── Cards ── */

.card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    padding: 1.5rem;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    margin-bottom: 1.25rem;
}

.card h3 {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 1.2rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--border);
}

h4 {
    font-size: 0.9rem;
    color: var(--text-secondary);
    margin: 1rem 0 0.5rem;
    font-weight: 600;
}

/* ── Tables ── */

table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
}

th,
td {
    text-align: left;
    padding: 0.85rem 1.25rem;
    border-bottom: 1px solid var(--border);
    font-size: 0.88rem;
    white-space: nowrap;
}

/* La primera columna (normalmente el nombre) puede envolver si es muy larga */
th:first-child,
td:first-child {
    white-space: normal;
}

th {
    font-weight: 600;
    color: var(--text-secondary);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    background: var(--bg);
}

th:first-child {
    border-top-left-radius: var(--radius);
}

th:last-child {
    border-top-right-radius: var(--radius);
}

td .btn-danger,
td .btn-success {
    padding: 0.3rem 0.6rem;
    font-size: 0.78rem;
}

td .btn-secondary {
    width: auto;
    margin-top: 0;
}

/* ── Calculator Layout ── */

/* Escritorio y tablet apaisada (≥960px): dos columnas — el formulario a la izquierda y
   el cuadro de costes a la derecha, arriba (sticky, ver `.calc-right`). En móvil/tablet
   vertical (≤959.98px) pasa a una sola columna apilada (formulario → costes → Guardar),
   ver más abajo. El botón Guardar (3.er hijo del grid) cae automáticamente bajo el
   formulario, en la columna izquierda. */
.calc-grid {
    display: grid;
    /* La derecha (costes) se lleva ~28% del ancho, con topes: nunca baja de 220px ni
       pasa de 340px. Así el formulario (izquierda) manda siempre — con una 2.ª columna
       fija de 340px, en tablet el formulario quedaba más estrecho que el cuadro de costes. */
    grid-template-columns: minmax(0, 1fr) clamp(220px, 28%, 340px);
    gap: 1.25rem;
    align-items: start;
}

/* Cuadro de costes: pegajoso (sticky) en la vista de 2 columnas — escritorio y tablet
   apaisada (≥960px) — para que el total siga visible mientras se rellena todo el
   formulario y al pulsar Guardar. El scroller es `.main-content` (overflow-y:auto), así
   que `top` es relativo a él. En móvil/tablet vertical se anula a `static` en el bloque
   de ≤959.98px: allí el cuadro va en el flujo, justo encima de Guardar. */
.calc-right {
    position: sticky;
    top: 1.5rem;
}

/* Guardar debajo del cuadro de costes, a lo ancho. SIN barra sticky: decisión de
   producto (revisión móvil 2026-06-28) — los costes se ven antes de llegar a Guardar. */
.calc-guardar-wrap button {
    width: 100%;
}

/* ── Results Section ── */

.results-section {
    border: 2px solid var(--accent);
    border-radius: var(--radius-lg);
}

/* Aviso del cálculo en vivo: alguna fila no entra en el total (incompleta, unidad
   incompatible o repetida). Tono ámbar, discreto. */
.calc-aviso-vivo {
    margin-top: 0.85rem;
    padding: 0.5rem 0.7rem;
    background: #fff6e6;
    border: 1px solid var(--gold);
    border-radius: var(--radius);
    color: #92400e;
    font-size: 0.8rem;
    line-height: 1.4;
}

.result-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.5rem 0;
}

.result-label {
    font-size: 0.88rem;
    color: var(--text-secondary);
}

.result-value {
    font-size: 0.95rem;
    font-weight: 600;
}

.result-bold {
    font-weight: 700;
    color: var(--text);
}

.result-green {
    color: var(--green);
}

.results-section hr {
    border: none;
    border-top: 1px solid var(--border);
    margin: 0.5rem 0;
}

.result-highlight {
    background: var(--accent);
    color: var(--on-accent);
    display: flex;
    justify-content: space-between;
    padding: 0.9rem 1rem;
    border-radius: var(--radius);
    margin: 1rem 0;
    font-weight: 700;
    font-size: 1.1rem;
}

/* ── Ingredient Rows (Calculator) ── */

.ingrediente-row {
    display: flex;
    gap: 0.75rem;
    align-items: center;
    margin-bottom: 0.5rem;
}

.ingrediente-row>div {
    margin-bottom: 0;
}

.ingrediente-row select,
.ingrediente-row input {
    margin-bottom: 0;
}

.ingrediente-row button {
    background: var(--danger);
    height: 36px;
    width: 36px;
    padding: 0;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.85rem;
    border-radius: 8px;
}

.ingrediente-row button:hover {
    background: var(--danger-hover);
}

/* Cantidad de una receta en el pedido (×N). */
.ped-row-cant-wrap {
    display: flex;
    align-items: center;
    gap: 2px;
    flex: 0 0 auto;
    color: var(--text-secondary);
    font-size: 0.85rem;
}

.ped-row-cant-wrap .ped-row-cant {
    width: 64px;
}

.ped-row-cant-fija {
    font-weight: 600;
    color: var(--text-secondary);
}

/* ── Modals ── */

.modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    /* Entrada suave (fundido del conjunto): antes aparecía de golpe, a diferencia del
       resto de la UI. Solo opacity (barato); prefers-reduced-motion ya lo anula. */
    animation: modalFade 0.16s ease;
}

@keyframes modalFade {
    from { opacity: 0; }
}

/* Alerta y confirmación van por encima del resto de modales: pueden lanzarse
   desde dentro de otro modal (p. ej. Ajustes) y deben quedar siempre visibles.
   El de eliminar cuenta se abre sobre Ajustes, así que comparte ese nivel. */
#modal-alert,
#modal-confirm,
#modal-eliminar-cuenta {
    z-index: 1100;
}

.modal-content {
    background: var(--card-bg);
    border-radius: var(--radius-lg);
    padding: 2rem;
    width: 500px;
    max-width: 90vw;
    max-height: 85vh;
    overflow-y: auto;
    box-shadow: var(--shadow-lg);
}

/* Con la barra dinámica del navegador, 85vh puede exceder el alto visible y recortar
   el pie del modal; dvh mide lo realmente visible. Solo ≥640: en móvil los modales
   van a pantalla completa con su propia regla. */
@media (min-width: 640px) {
    @supports (height: 1dvh) {
        .modal-content {
            max-height: 88dvh;
        }
    }
}

.modal-content h3 {
    font-size: 1.1rem;
    margin-bottom: 1.5rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--border);
}

/* DECISIÓN del dueño (2026-07-07): en los modales largos, el TÍTULO queda fijo arriba
   y los BOTONES de acción fijos abajo mientras el cuerpo scrollea (patrón estándar de
   diálogo; antes había que llegar al fondo para encontrar Guardar/Cancelar). Nota: esto
   NO reabre el veto de barras fijas de la Calculadora (aquello es una vista, no un
   modal). Técnica: sticky + márgenes negativos que "extienden" el elemento sobre el
   padding del scroller, con fondo opaco para tapar el contenido que pasa por debajo. */
.modal-content > h3 {
    position: sticky;
    top: -2rem;              /* = -padding-top del .modal-content */
    z-index: 4;
    background: var(--card-bg);
    margin: -2rem -2rem 1.5rem;
    padding: 2rem 2rem 0.5rem;
}

.modal-content > .modal-actions {
    position: sticky;
    bottom: -2rem;           /* = -padding-bottom del .modal-content */
    z-index: 4;
    background: var(--card-bg);
    margin: 1.5rem -2rem -2rem;
    padding: 0.75rem 2rem 2rem;
    border-top: 1px solid var(--border);
}

.modal-wide {
    width: 700px;
}

/* Botón × de cierre de los modales full-screen protegidos (pedido, editar preparación):
   salida obvia arriba a la derecha. Su .modal-content necesita posicionarse para anclarlo. */
#modal-pedido .modal-content,
#modal-editar-preparacion .modal-content {
    position: relative;
}

/* Sticky (no absolute): con la cabecera fija, el × debe seguir visible durante el
   scroll — antes desaparecía por arriba y había que re-scrollear para salir. El float
   lo saca del flujo horizontal (el h3 le reserva hueco con su padding-right). */
.modal-close-x {
    position: sticky;
    top: -1.4rem;            /* -padding(2rem) + 0.6rem de separación visual */
    float: right;
    margin: -1.4rem -1.4rem 0 0;
    width: 44px;
    height: 44px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--card-bg);
    color: var(--text-secondary);
    border: none;
    border-radius: var(--radius);
    font-size: 1.6rem;
    line-height: 1;
    cursor: pointer;
    z-index: 6;
}

.modal-close-x:hover {
    background: rgba(var(--accent-rgb), 0.06);
    color: var(--text);
}

/* Que el título no quede debajo del × (deja hueco a su derecha). */
#modal-pedido .modal-content > h3,
#modal-editar-preparacion .modal-content > h3 {
    padding-right: 2.5rem;
}

.modal-actions {
    display: flex;
    gap: 0.75rem;
    margin-top: 1.5rem;
    justify-content: flex-end;
}

.modal-actions button {
    width: auto;
    margin-top: 0;
}

.modal-actions .btn-secondary {
    width: auto;
    border-style: solid;
}

/* ── Edit inputs inline ── */

.edit-input {
    margin-bottom: 0 !important;
    padding: 0.4rem 0.5rem;
    font-size: 0.85rem;
}

/* Cantidad + unidad de la edición inline de Materias: flex en vez de anchos en %
   (en el modo tarjeta móvil los % se descuadraban dentro de la celda flex). */
.edit-cant-wrap {
    display: flex;
    gap: 0.4rem;
    align-items: center;
    min-width: 0;
}

.edit-cant-wrap input {
    flex: 1;
    min-width: 0;
}

.edit-cant-wrap select {
    flex: 0 0 auto;
    width: auto;
}

/* Contacto sin valor (email/teléfono vacío) */
.sin-info {
    color: var(--text-secondary);
    font-style: italic;
}

/* ── Barra de filtros de los listados (antes: <div> con estilos inline y un selector
   frágil div[style*="flex-wrap"] en el CSS responsive) ── */
.filtros-bar {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
    align-items: flex-end;
}

.filtros-bar .filtro-campo {
    flex: 1 1 160px;
    min-width: 0;
}

.filtros-bar .filtro-fijo {
    flex: 0 0 auto;
}

.filtros-bar input,
.filtros-bar select {
    margin-bottom: 0;
}

/* ── Acciones cell ── */

.acciones-cell {
    white-space: nowrap;
    vertical-align: middle;
}

.acciones-inner {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

/* ── Utilities ── */

.hidden {
    display: none !important;
}

#error-msg,
#signup-error-msg,
#recover-msg,
#otp-msg,
#reset-msg {
    color: var(--danger);
    font-size: 0.85rem;
    min-height: 1.2em;
}

/* Los mismos elementos cuando muestran un mensaje de éxito o informativo (p. ej.
   "enlace enviado" en recuperación, o el aviso del email del pago en registro). */
.msg-ok {
    color: var(--green) !important;
}

.msg-info {
    color: var(--text-secondary) !important;
}

.empty-state .empty-cta {
    margin-top: 0.8rem;
}

.empty-state {
    text-align: center;
    /* Aire vertical extra y línea acotada: en una card ancha, una sola línea de
       0.9rem flotando en el vacío se perdía (primer contacto de una cuenta nueva). */
    padding: 2.5rem 1rem;
    color: var(--text-secondary);
    font-size: 0.92rem;
    max-width: 560px;
    margin-inline: auto;
}

/* Tabla atenuada mientras se espera la respuesta de un filtro (Pedidos, Histórico). */
.tabla-cargando {
    opacity: 0.45;
    transition: opacity 0.15s ease;
    pointer-events: none;
}

/* Paginación de listados (Histórico, Pedidos): controles centrados bajo la tabla. */
.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin-top: 1rem;
}

/* Los botones son .btn-secondary, que por defecto va a width:100% + margin-top; en la
   barra de paginación deben quedar compactos y en línea con el indicador. */
.pagination .btn-secondary {
    width: auto;
    margin-top: 0;
}

.pagination-info {
    font-size: 0.85rem;
    color: var(--text-secondary);
    white-space: nowrap;
}

/* ── Edit Indicator (Calculator) ── */

.edit-indicator {
    background: linear-gradient(135deg, #fff8f0, #fff3e6);
    border: 1px solid var(--gold);
    border-left: 4px solid var(--gold);
    border-radius: var(--radius);
    padding: 0.75rem 1.25rem;
    margin-bottom: 1.25rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    animation: slideDown 0.3s ease;
}

.edit-indicator-content {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.9rem;
    color: var(--text);
}

.edit-indicator-content svg {
    color: var(--gold-text);
    flex-shrink: 0;
}

.edit-indicator .btn-secondary {
    width: auto;
    margin-top: 0;
    border-style: solid;
    white-space: nowrap;
}

@keyframes slideDown {
    from {
        opacity: 0;
        transform: translateY(-10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* ── Estilo "Rota" para recetas incompletas (ingredientes a 0 o sin ingredientes) ── */

.badge-rota {
    display: inline-block;
    background: #fef3c7;
    color: #92400e;
    border: 1px solid #fcd34d;
    padding: 0.1rem 0.45rem;
    border-radius: var(--radius-pill);
    font-size: 0.75rem;
    font-weight: 600;
    margin-left: 0.35rem;
    white-space: nowrap;
    vertical-align: middle;
}

/* En Producción el badge es un botón: lleva directo a arreglar la receta en la
   Calculadora. Anula la altura táctil global de button en móvil. El subrayado lo
   diferencia del MISMO badge usado como <span> informativo en el modal Ver Receta. */
button.badge-rota {
    cursor: pointer;
    min-height: 0;
    text-decoration: underline;
}

button.badge-rota:hover {
    background: #fde68a;
}

tr.row-rota {
    background: rgba(254, 243, 199, 0.6);
}

tr.row-rota:hover {
    background: rgba(253, 230, 138, 0.55);
}

/* Filas clicables que abren el detalle (sustituyen al botón "ojo"). */
tr.row-clickable {
    cursor: pointer;
}

tr.row-clickable:hover {
    background: rgba(var(--accent-rgb), 0.045);
}

tr.row-clickable:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

td.ing-roto {
    color: #92400e;
    font-weight: 600;
}

.rota-warning-banner {
    background: linear-gradient(135deg, #fffbeb, #fef3c7);
    border: 1px solid #fcd34d;
    border-left: 4px solid #d97706;
    border-radius: var(--radius);
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
    color: #92400e;
    font-size: 0.9rem;
    font-weight: 500;
}

/* ── Banner de sustitución de materia prima ── */

.sustitucion-banner {
    background: linear-gradient(135deg, #EFF4F9, #E4EDF5);
    border: 1px solid #9FB4C9;
    border-left: 4px solid var(--info);
    border-radius: var(--radius);
    padding: 0.75rem 1.25rem;
    margin-bottom: 1.25rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    animation: slideDown 0.3s ease;
}

.sustitucion-banner-text {
    font-size: 0.9rem;
    color: var(--text);
}

.sustitucion-banner .btn-secondary {
    width: auto;
    margin-top: 0;
    border-style: solid;
    white-space: nowrap;
}

@media (max-width: 720px) {
    .sustitucion-banner {
        flex-direction: column;
        align-items: stretch;
    }
    .sustitucion-banner .btn-secondary {
        width: 100%;
    }
}

/* ── Modal Vertical Actions ── */

.modal-actions-vertical {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-top: 1rem;
}

.btn-full {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.75rem 1.2rem;
    font-size: 0.9rem;
}

.btn-full svg {
    flex-shrink: 0;
}

.btn-cancel {
    background: transparent;
    color: var(--text-secondary);
    border: 1px solid var(--border);
}

.btn-cancel:hover {
    background: var(--bg);
    color: var(--text);
}

/* ── Toast Notifications ── */

#toast-container {
    position: fixed;
    top: 1.5rem;
    right: 1.5rem;
    z-index: 2000;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.toast {
    background: var(--green);
    color: var(--on-accent);
    padding: 0.75rem 1.25rem;
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    font-size: 0.9rem;
    font-weight: 500;
    opacity: 0;
    transform: translateX(30px);
    transition: opacity 0.3s ease, transform 0.3s ease;
    max-width: 360px;
}

.toast-show {
    opacity: 1;
    transform: translateX(0);
}

.toast-error {
    background: var(--danger);
}

/* Aviso persistente de "sin conexión" (lo gestiona ui.js con los eventos
   online/offline): por encima de toasts y modales — sin red, nada funciona. */
#offline-banner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 2100;
    background: var(--accent-dark);
    color: var(--on-accent);
    text-align: center;
    font-size: 0.85rem;
    font-weight: 500;
    padding: 0.5rem calc(1rem + env(safe-area-inset-right)) 0.5rem calc(1rem + env(safe-area-inset-left));
    padding-top: calc(0.5rem + env(safe-area-inset-top));
}

/* Aviso informativo (no es un éxito): un mensaje neutro de información. */
.toast-info {
    background: var(--accent);
}

.btn-danger.btn-small svg {
    display: block;
}

/* ── Modal Ver Receta ── */

.ver-receta-section {
    margin-bottom: 1.25rem;
}

.ver-receta-section h4 {
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-secondary);
}

.ver-receta-table {
    width: 100%;
    margin-bottom: 0;
    /* table-layout fijo + wrap: en móvil, los nombres largos de receta/ingrediente
       ya no desbordan ni comprimen las columnas; parten de línea dentro de su celda. */
    table-layout: fixed;
}

.ver-receta-table th,
.ver-receta-table td {
    padding: 0.5rem 0.75rem;
    font-size: 0.85rem;
    /* Sin white-space:normal, el nowrap global de th/td anulaba el overflow-wrap
       y los nombres largos desbordaban la tabla del modal. */
    white-space: normal;
    overflow-wrap: anywhere;
}

.ver-receta-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
}

.ver-receta-item {
    display: flex;
    justify-content: space-between;
    padding: 0.5rem 0.75rem;
    background: var(--bg);
    border-radius: var(--radius);
}

.ver-label {
    font-size: 0.85rem;
    color: var(--text-secondary);
}

.ver-value {
    font-size: 0.85rem;
    font-weight: 600;
}

.ver-value-bold {
    font-weight: 700;
    color: var(--text);
}

.ver-value-green {
    font-weight: 700;
    color: var(--green);
}

.ver-receta-highlight {
    background: var(--accent);
    color: var(--on-accent);
    border-radius: var(--radius);
    padding: 0.75rem 1rem;
    margin: 1rem 0 0.75rem;
}

.ver-receta-highlight-row {
    display: flex;
    justify-content: space-between;
    padding: 0.3rem 0;
    font-size: 0.9rem;
}

.ver-receta-precio {
    font-weight: 700;
    font-size: 1.1rem;
    padding: 0.5rem 0 0.3rem;
    border-top: 1px solid rgba(255,255,255,0.2);
    margin-top: 0.3rem;
}

.ver-receta-unidad {
    font-size: 0.85rem;
    opacity: 0.85;
}

.ver-receta-fecha {
    text-align: right;
    font-size: 0.8rem;
    color: var(--text-secondary);
}

/* ── Pedidos: estado badges ── */

.estado-badge {
    display: inline-block;
    padding: 0.2rem 0.6rem;
    border-radius: var(--radius-pill);
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--on-accent);
}

/* Dorado oscurecido: con var(--gold) el texto blanco quedaba a ~2.5:1 de
   contraste (por debajo del AA de WCAG); este tono ronda el 5:1. */
.estado-pendiente   { background: var(--gold-text); }
.estado-preparando  { background: var(--accent); }
.estado-entregado   { background: var(--green); }
.estado-cancelado   { background: var(--text-secondary); }

/* Promoción: el pedido se regala (total 0 €); el badge lo explica en la tabla. */
.promo-badge {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border-radius: var(--radius-pill);
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--gold-text);
    border: 1px solid var(--gold);
    vertical-align: middle;
}

.ped-row-locked {
    display: inline-block;
    margin-left: 0.4rem;
    font-size: 0.7rem;
    color: var(--text-secondary);
}

/* ── Acciones cell: igualar tamaño de los 3 botones ── */

.acciones-cell .btn-secondary {
    width: auto;
    margin-top: 0;
    padding: 0.35rem 0.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Con la fila entera clicable (abre el detalle), los botones internos necesitan un
   hover claramente propio para no confundir "ver" con "editar/borrar". */
.acciones-cell button:hover {
    box-shadow: 0 0 0 2px rgba(var(--accent-rgb), 0.22);
}

/* Zona táctil de 44px en los botones de acción de fila también en TABLET (>=640px),
   no solo en el móvil estrecho: la tablet es táctil igual que el móvil y un botón de
   ~30px es difícil de acertar con el dedo. Además se separan más ('Borrar' junto a
   'Editar') para no pulsar el equivocado. En escritorio (>=1024px, con ratón) se
   mantiene el tamaño compacto original. */
@media (min-width: 640px) and (max-width: 1023.98px) {
    .acciones-inner {
        gap: 0.6rem;
    }

    .acciones-cell .btn-small {
        min-width: 44px;
        min-height: 44px;
    }
}

/* ── Modal Pedido: más ancho + resumen en una fila ── */

#modal-pedido .modal-content {
    width: 880px;
}

#modal-pedido .result-highlight {
    gap: 0.75rem;
    align-items: stretch;
}

#modal-pedido .ver-receta-highlight-row {
    flex: 1;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 0.25rem 0.5rem;
    gap: 0.25rem;
}

#modal-pedido .ver-receta-precio {
    border-top: none;
    margin-top: 0;
    border-left: 1px solid rgba(255, 255, 255, 0.25);
}

/* ══════════════════════════════════════════════════════════
   RESPONSIVE — Checkpoint 0: scroll horizontal en tablas
   El wrapper evita que la tabla desborde la .card cuando
   crecen las columnas (caso Recetas con 11 cols).
   ══════════════════════════════════════════════════════════ */

.table-wrap {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    /* Sombra sutil en los bordes para indicar scroll disponible */
    background:
        linear-gradient(to right, var(--card-bg) 30%, rgba(255, 255, 255, 0)),
        linear-gradient(to right, rgba(var(--accent-rgb), 0.08), rgba(255, 255, 255, 0) 30%) 0 100%,
        linear-gradient(to left, var(--card-bg) 30%, rgba(255, 255, 255, 0)) 100% 0,
        linear-gradient(to left, rgba(var(--accent-rgb), 0.08), rgba(255, 255, 255, 0) 30%) 100% 100%;
    background-repeat: no-repeat;
    background-size: 30px 100%, 14px 100%, 30px 100%, 14px 100%;
    background-attachment: local, scroll, local, scroll;
}

.table-wrap table {
    min-width: 100%;
}

/* ══════════════════════════════════════════════════════════
   RESPONSIVE — Checkpoint 1: top-bar + sidebar drawer
   ══════════════════════════════════════════════════════════ */

/* Top-bar móvil: oculta en desktop por defecto */
.mobile-topbar {
    display: none;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 1rem;
    background: var(--sidebar-bg);
    border-bottom: 1px solid var(--sidebar-line);
    position: sticky;
    top: 0;
    z-index: 100;
}

.mobile-menu-btn {
    background: transparent;
    color: var(--sidebar-text);
    border: none;
    padding: 0.4rem;
    border-radius: 8px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.mobile-menu-btn:hover {
    background: var(--sidebar-hover);
}

.mobile-topbar-brand {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-weight: 700;
    color: #F3E9DA;
    font-size: 0.95rem;
}

.sidebar-backdrop {
    /* No usamos display:none (no es animable): con visibility+opacity el fade
       sincroniza con el deslizamiento del drawer al abrir y al cerrar. */
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 90;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.25s ease, visibility 0.25s ease;
}

.sidebar-backdrop.show {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
}

/* ─── Tablet y móvil (< 1024px): sidebar como drawer ─── */
@media (max-width: 1023.98px) {
    body {
        flex-direction: column;
    }

    #app-view {
        flex-direction: column;
        height: 100vh;
        height: 100dvh;
    }

    .mobile-topbar {
        display: flex;
    }

    .sidebar {
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        width: 260px;
        max-width: 80vw;
        transform: translateX(-100%);
        transition: transform 0.25s ease;
        /* Por encima de la topbar sticky (100): el empate anterior se resolvía solo
           por orden del DOM (frágil). */
        z-index: 120;
        box-shadow: var(--shadow-lg);
    }

    .sidebar.open {
        transform: translateX(0);
    }

    .main-content {
        padding: 1.25rem 1rem 6rem;
        flex: 1;
        min-height: 0;
    }

    /* Ocultar el branding original de la sidebar en móvil (ya está en top-bar) */
    .sidebar .sidebar-brand {
        display: none;
    }
}

/* ─── Móvil estrecho (< 640px) ─── */
@media (max-width: 639.98px) {
    .main-content {
        padding: 1rem 0.75rem 6rem;
    }

    .page-header h2 {
        font-size: 1.35rem;
    }

    .card {
        padding: 1rem;
        margin-bottom: 1rem;
    }
}

/* ══════════════════════════════════════════════════════════
   RESPONSIVE — Checkpoint 2: forms, inputs, modales
   ══════════════════════════════════════════════════════════ */

@media (max-width: 639.98px) {
    /* Stack vertical de form-row */
    .form-row {
        flex-direction: column;
        gap: 0;
    }

    /* font-size 16px para evitar zoom en iOS */
    input,
    select,
    textarea {
        font-size: 16px;
        padding: 0.65rem 0.75rem;
        min-height: 44px;
    }

    /* Botones con altura mínima táctil */
    button {
        min-height: 44px;
        font-size: 0.95rem;
    }

    .btn-small {
        min-height: 36px;
        padding: 0.4rem 0.7rem;
    }

    /* Modales: full-screen en móvil */
    .modal-content,
    .modal-wide,
    #modal-pedido .modal-content {
        width: 100%;
        max-width: 100%;
        height: 100%;
        max-height: 100vh;
        max-height: 100dvh;
        border-radius: 0;
        padding: 1.25rem 1rem;
        overflow-y: auto;
    }

    .modal {
        align-items: stretch;
        justify-content: stretch;
    }

    /* Acciones del modal apiladas en lugar de side-by-side */
    .modal-actions {
        flex-direction: column-reverse;
        gap: 0.5rem;
    }

    .modal-actions button,
    .modal-actions .btn-secondary {
        width: 100%;
    }

    /* Auth form en pantalla completa */
    .auth-container {
        width: 100%;
        max-width: 380px;
        padding: 2rem 1.5rem;
        margin: 1rem;
    }
}

/* Tablet: modales más generosos pero no full-screen */
@media (min-width: 640px) and (max-width: 1023.98px) {
    /* El modal estándar de 500px producía doble scroll con listas internas pese a
       haber sitio de sobra. */
    .modal-content {
        width: min(560px, 92vw);
    }

    .modal-wide,
    #modal-pedido .modal-content {
        width: 92vw;
        max-width: 720px;
    }

    /* Ajustes: con la nav de 180px dentro de 720px, el panel quedaba a ~510px. */
    .modal-wide.ajustes-modal {
        width: min(860px, 94vw);
        max-width: 94vw;
    }

    /* Inputs un poco más grandes pero sin zoom iOS */
    input,
    select,
    textarea {
        font-size: 16px;
    }
}

/* ══════════════════════════════════════════════════════════
   RESPONSIVE — Checkpoint 3: tablas como cards en móvil Y TABLET
   (auditoría 2026-07: el rango tablet 640-1023px heredaba la tabla de
   escritorio con scroll horizontal; se extiende el patrón tarjeta hasta 1024px)
   ══════════════════════════════════════════════════════════ */

@media (max-width: 1023.98px) {
    /* Quitamos el scroll horizontal: las cards no lo necesitan */
    .table-wrap {
        overflow-x: visible;
        background: none;
    }

    .table-wrap table,
    .table-wrap thead,
    .table-wrap tbody,
    .table-wrap tr,
    .table-wrap td {
        display: block;
        width: 100%;
    }

    /* Ocultar el thead (los labels van en cada celda via data-label) */
    .table-wrap thead {
        position: absolute;
        left: -9999px;
        top: -9999px;
    }

    /* Cada fila como tarjeta */
    .table-wrap tbody tr {
        background: var(--bg);
        border: 1px solid var(--border);
        border-radius: var(--radius);
        margin-bottom: 0.75rem;
        padding: 0.75rem 0.9rem;
        box-shadow: var(--shadow-sm);
    }

    .table-wrap tbody tr:last-child {
        margin-bottom: 0;
    }

    /* Celdas con label encima del valor */
    .table-wrap tbody td {
        border-bottom: 1px dashed var(--border);
        padding: 0.5rem 0;
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 0.5rem;
        font-size: 0.9rem;
        text-align: right;
        white-space: normal;
    }

    .table-wrap tbody td:last-child {
        border-bottom: none;
        padding-bottom: 0;
    }

    .table-wrap tbody td:first-child {
        padding-top: 0;
    }

    /* La 1.ª celda (el nombre) como TÍTULO de la tarjeta: en bloque, a la izquierda
       y destacada — antes competía a 0.9rem con su etiqueta «NOMBRE» y la tarjeta no
       tenía jerarquía. Solo celdas con data-label (las filas de edición/vacías no). */
    .table-wrap tbody td:first-child[data-label] {
        display: block;
        text-align: left;
        font-size: 1.02rem;
        padding-bottom: 0.45rem;
    }

    .table-wrap tbody td:first-child[data-label]::before {
        content: none;
    }

    /* Afordancia de tarjeta pulsable (abre el detalle): chevron a la derecha del
       título, solo en las filas clicables. */
    .table-wrap tbody tr.row-clickable td:first-child[data-label]::after {
        content: '›';
        float: right;
        color: var(--text-secondary);
        font-size: 1.25rem;
        line-height: 1;
    }

    .table-wrap tbody td::before {
        content: attr(data-label);
        font-size: 0.72rem;
        text-transform: uppercase;
        letter-spacing: 0.5px;
        font-weight: 600;
        color: var(--text-secondary);
        text-align: left;
        flex-shrink: 0;
    }

    /* Celdas sin data-label (empty-state, edit row): centradas */
    .table-wrap tbody td:not([data-label]) {
        justify-content: center;
        text-align: center;
    }

    .table-wrap tbody td:not([data-label])::before {
        content: none;
    }

    /* Empty-state: la fila no es una card */
    .table-wrap tbody tr.empty-row {
        background: none;
        border: none;
        box-shadow: none;
        padding: 0;
    }

    /* Celda de acciones: alinear los botones a la derecha */
    .table-wrap tbody td.acciones-cell {
        flex-wrap: wrap;
        justify-content: flex-end;
    }

    .table-wrap tbody td.acciones-cell::before {
        margin-right: auto;
    }
}

/* ══════════════════════════════════════════════════════════
   RESPONSIVE — Checkpoint 4: calculadora
   ══════════════════════════════════════════════════════════ */

/* Móvil y tablet en vertical (≤959.98px): stack vertical (align-items:stretch → ancho
   completo) — formulario, luego el cuadro de costes y, debajo, el botón Guardar (así los
   costes se ven antes de guardar). Por debajo de ~960px, con la barra lateral fija de
   220px el contenido queda demasiado estrecho para 2 columnas: la fila de ingredientes se
   aplastaba (los campos quedaban recortados a una letra). El grid de 2 columnas (costes a
   la derecha) entra desde 960px, que cubre la tablet apaisada y el escritorio. Aquí el
   cuadro de costes va en el FLUJO, justo encima de Guardar (no sticky) — decisión de
   producto para móvil (revisión móvil 2026-06-28); el sticky es solo para la vista ancha. */
@media (max-width: 959.98px) {
    .calc-grid {
        display: flex;
        flex-direction: column;
        align-items: stretch;
    }

    /* Anula el sticky del cuadro de costes: en una columna va en el flujo. */
    .calc-right {
        position: static;
    }
}

/* Móvil: ingrediente-row se adapta */
@media (max-width: 639.98px) {
    .ingrediente-row {
        flex-wrap: wrap;
        gap: 0.5rem;
        padding: 0.6rem;
        background: var(--bg);
        border-radius: var(--radius);
        margin-bottom: 0.6rem;
    }

    /* El select de receta/ingrediente ocupa toda la fila */
    .ingrediente-row > div:first-child {
        flex: 1 1 100% !important;
    }

    /* Cantidad y unidad comparten la siguiente fila */
    .ingrediente-row > div:nth-child(2) {
        flex: 1 1 0 !important;
        min-width: 0;
    }

    .ingrediente-row > div:nth-child(3) {
        flex: 0 0 90px !important;
    }

    /* Botón X siempre a la derecha */
    .ingrediente-row > div:last-child {
        flex: 0 0 auto;
    }

    .ingrediente-row select,
    .ingrediente-row input {
        font-size: 16px;
        min-height: 44px;
    }

    /* Indicador de edición */
    .edit-indicator {
        flex-direction: column;
        align-items: stretch;
        gap: 0.5rem;
    }

    .edit-indicator .btn-secondary {
        width: 100%;
    }
}

/* ══════════════════════════════════════════════════════════
   RESPONSIVE — Checkpoint 5: modal pedidos + result-highlight
   ══════════════════════════════════════════════════════════ */

/* result-highlight del modal pedido: stack vertical en pantallas pequeñas */
@media (max-width: 767.98px) {
    #modal-pedido .result-highlight {
        flex-direction: column;
    }

    #modal-pedido .ver-receta-highlight-row {
        flex-direction: row;
        justify-content: space-between;
        text-align: left;
        padding: 0.5rem 0;
        border-left: none;
        border-bottom: 1px solid rgba(255, 255, 255, 0.25);
    }

    #modal-pedido .ver-receta-highlight-row:last-child {
        border-bottom: none;
    }

    #modal-pedido .ver-receta-precio {
        border-top: 1px solid rgba(255, 255, 255, 0.25);
        margin-top: 0.25rem;
        padding-top: 0.5rem;
    }
}

/* Móvil: filas internas del modal pedido + grid de Ver Receta */
@media (max-width: 639.98px) {
    /* Filas de receta del modal de pedido */
    #ped-recetas-container .ingrediente-row {
        flex-wrap: wrap;
        gap: 0.5rem;
        padding: 0.6rem;
        background: var(--bg);
        border-radius: var(--radius);
    }

    #ped-recetas-container .ingrediente-row > div:first-child {
        flex: 1 1 100% !important;
    }

    /* Segunda fila visual: cantidad (×N) + coste de la línea + botón quitar. */
    #ped-recetas-container .ingrediente-row > .ped-row-cant-wrap {
        flex: 0 0 auto !important;
    }

    #ped-recetas-container .ingrediente-row > div:nth-child(3) {
        flex: 1 1 auto !important;
        text-align: left !important;
    }

    /* Ver Receta / Ver Pedido: grid a 1 col */
    .ver-receta-grid {
        grid-template-columns: 1fr;
    }

    /* Botón "Nuevo Pedido" full width */
    #btn-nuevo-pedido {
        width: 100%;
    }

    /* Filtros de pedidos apilados */
    #tab-pedidos .filtros-bar {
        flex-direction: column;
        align-items: stretch;
    }
}

/* ══════════════════════════════════════════════════════════
   RESPONSIVE — Checkpoint 6: pulido (toasts, auth, varios)
   ══════════════════════════════════════════════════════════ */

@media (max-width: 639.98px) {
    /* Toasts: ocupan ancho casi completo */
    #toast-container {
        top: auto;
        bottom: 1rem;
        right: 0.75rem;
        left: 0.75rem;
        align-items: stretch;
    }

    .toast {
        max-width: none;
        text-align: center;
    }

    /* Auth view: el fondo color sigue, pero la container responsive */
    #auth-view {
        padding: 1rem;
    }

    .auth-container {
        padding: 2rem 1.25rem;
    }

    /* Página: títulos y descripciones algo más compactos */
    .page-header {
        margin-bottom: 1rem;
    }

    /* Card header con menos padding */
    .card h3 {
        font-size: 0.95rem;
        margin-bottom: 0.9rem;
    }

    /* Botón submit de formularios al ancho completo */
    form > button[type="submit"] {
        width: 100%;
    }
}

/* Tablet: ajustes finos */
@media (min-width: 640px) and (max-width: 1023.98px) {
    .main-content {
        /* El colchón inferior de 6rem es el que evita que el launcher del chat
           tape la última fila de las tablas (como en desktop y móvil). */
        padding: 1.5rem 1.5rem 6rem;
    }

    /* Form rows en tablet: lado a lado pero con salto de línea permitido — la fila
       de 4 campos del modal de pedido (Margen/Descuento/Embalaje/Transporte) pasa
       a 2×2 en vez de convivir apretada; las filas de 2 campos no cambian. */
    .form-row {
        gap: 0.75rem;
        flex-wrap: wrap;
    }

    .form-row > div {
        flex: 1 1 calc(50% - 0.75rem);
    }

    /* Login/registro: la tarjeta de 380px sobre un fondo vacío desaprovechaba la
       tablet (primera pantalla del embudo). */
    .auth-container {
        width: min(480px, 90vw);
    }

    /* Toasts: ni la esquina de escritorio (lejos del pulgar, tapando el launcher de
       onboarding) ni el ancho completo de móvil — abajo y centrados. */
    #toast-container {
        top: auto;
        bottom: calc(1.25rem + env(safe-area-inset-bottom));
        left: 50%;
        right: auto;
        transform: translateX(-50%);
        align-items: center;
    }

    .toast {
        max-width: min(480px, 90vw);
    }

    /* Gráficas algo más altas: al ensancharse en tablet quedaban achatadas. */
    .dash-canvas-wrap {
        height: 300px;
    }

    /* Dato clave (Coste/Precio total) con el escalón intermedio que faltaba. */
    .result-highlight {
        font-size: 1.5rem;
    }
}

/* Segmented control: toggle de opciones en forma de pastillas (lo usa el selector
   de periodo del panel de Inicio). El radio real se mantiene (invisible y
   superpuesto) para conservar accesibilidad y teclado. */
.segmented {
    display: inline-flex;
    margin-top: 0.4rem;
    padding: 3px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
}

.segmented-option {
    position: relative;
    margin: 0;
}

.segmented-option input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.segmented-option span {
    display: block;
    padding: 0.45rem 1.1rem;
    border-radius: var(--radius-pill);
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--text-secondary);
    white-space: nowrap;
    transition: background 0.15s, color 0.15s;
}

.segmented-option:hover span {
    color: var(--accent);
}

.segmented-option input:checked + span {
    background: var(--accent-soft);
    color: var(--on-accent);
    box-shadow: var(--shadow-sm);
}

.segmented-option input:focus-visible + span {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* ── Elaboraciones: tarjetas de tipo + ficha en dos campos + chips ── */

/* Tarjetas-radio del tipo: cada opción explica su uso (guardar la elaboración de
   una receta de Producción vs. digitalizar el recetario propio). El radio real va
   invisible encima, como en .segmented. */
.rp-tipo-cards {
    display: grid;
    gap: 0.75rem;
    margin-top: 0.4rem;
    margin-bottom: 1rem;
}

@media (min-width: 640px) {
    .rp-tipo-cards {
        grid-template-columns: 1fr 1fr;
    }
}

.rp-tipo-card {
    position: relative;
    margin: 0;
}

.rp-tipo-card input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.rp-tipo-card-body {
    display: block;
    height: 100%;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}

.rp-tipo-card-body strong {
    display: block;
    font-size: 0.92rem;
    margin-bottom: 0.2rem;
}

.rp-tipo-card-body small {
    display: block;
    font-size: 0.8rem;
    line-height: 1.4;
    color: var(--text-secondary);
}

.rp-tipo-card:hover .rp-tipo-card-body {
    border-color: var(--accent);
}

/* Seleccionada: doble trazo marino (borde + sombra interior, sin saltos de layout)
   y tinte suave del fondo. */
.rp-tipo-card input:checked + .rp-tipo-card-body {
    border-color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
    background: rgba(var(--accent-rgb), 0.05);
}

.rp-tipo-card input:checked + .rp-tipo-card-body strong {
    color: var(--accent);
}

.rp-tipo-card input:focus-visible + .rp-tipo-card-body {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Las independientes escriben Ingredientes (opcional) y Elaboración por separado; al
   guardar se unen en el único `contenido` de la API. En pantalla ancha van lado a
   lado como una ficha de receta clásica (ingredientes estrechos, pasos anchos);
   en tablet/móvil se apilan. Las enlazadas (rp-solo-pasos) solo ven Elaboración. */
.rp-campos {
    display: grid;
    gap: 1rem;
}

/* El hueco entre campos lo pone el gap del grid, no el margen global del textarea
   (sumados dejaban un doble hueco antes del botón/nota inferior). */
.rp-campos textarea {
    margin-bottom: 0;
}

@media (min-width: 960px) {
    .rp-campos {
        grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
    }
}

.rp-campos.rp-solo-pasos {
    grid-template-columns: minmax(0, 1fr);
}

/* Apunte "(opcional...)" dentro de un label, sin el peso del propio label. */
.label-opcional {
    color: var(--text-secondary);
    font-weight: 400;
}

/* Tipo de elaboración como chip suave (mismo lenguaje que los estados de
   Sugerencias): enlazada = acero, independiente = neutro. */
.rp-tipo-chip {
    display: inline-block;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.12rem 0.55rem;
    border-radius: var(--radius-pill);
    white-space: nowrap;
}

.rp-tipo-enlazada { background: #E8EFF6; color: #33608D; }
.rp-tipo-independiente { background: #eef0f3; color: #5b6470; }

/* ── Chatbot de ayuda ── */

#chat-launcher {
    position: fixed;
    bottom: 24px;
    right: 24px;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    border: none;
    /* Marino pleno como los botones de acción (07-11): en soft quedaba descolgado. */
    background: var(--accent);
    color: var(--on-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    /* Por debajo de los modales (1000) y del aviso de cookies (1300): flotando
       por encima tapaba sus botones en móvil (p. ej. el "Entendido" del banner). */
    cursor: pointer;
    box-shadow: var(--shadow-lg);
    transition: background 0.15s, transform 0.15s;
    z-index: 950;
}

#chat-launcher:hover {
    background: var(--accent-hover);
    transform: translateY(-2px);
}

/* En móvil, dentro de «IA · Recetas» el lanzador de Jarvis caía justo encima del botón
   de enviar del chat de Atlas (ambos abajo a la derecha) y casi no se podía pulsar:
   ahí se oculta; la ayuda sigue disponible en el resto de pestañas. */
@media (max-width: 767.98px) {
    body:has(#tab-recetas-chat.active) #chat-launcher {
        display: none;
    }
}

#chat-panel {
    position: fixed;
    bottom: 24px;
    right: 24px;
    width: 380px;
    max-width: calc(100vw - 32px);
    height: 540px;
    max-height: calc(100vh - 48px);
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* Sobre el lanzador de onboarding (951): en móvil el panel es a pantalla completa
       y el anillo de «Primeros pasos» tapaba su X de cerrar. Bajo modales (1000). */
    z-index: 952;
}

#chat-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.9rem 1.1rem;
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 600;
}

#chat-close {
    background: none;
    border: none;
    color: var(--on-accent);
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 0.25rem;
}

#chat-messages {
    flex: 1;
    overflow-y: auto;
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.chat-msg {
    max-width: 85%;
    padding: 0.6rem 0.85rem;
    border-radius: 14px;
    font-size: 0.9rem;
    line-height: 1.45;
    word-wrap: break-word;
}

.chat-msg-bot {
    align-self: flex-start;
    background: var(--panel);
    color: var(--text);
    border-bottom-left-radius: 4px;
}

.chat-msg-user {
    align-self: flex-end;
    background: var(--accent);
    color: var(--on-accent);
    border-bottom-right-radius: 4px;
}

.chat-typing {
    display: flex;
    gap: 4px;
    align-items: center;
}

.chat-typing span {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--text-secondary);
    opacity: 0.5;
    animation: chat-bounce 1.2s infinite ease-in-out;
}

.chat-typing span:nth-child(2) { animation-delay: 0.2s; }
.chat-typing span:nth-child(3) { animation-delay: 0.4s; }

@keyframes chat-bounce {
    0%, 60%, 100% { transform: translateY(0); opacity: 0.5; }
    30% { transform: translateY(-4px); opacity: 1; }
}

#chat-form {
    display: flex;
    gap: 0.5rem;
    padding: 0.75rem;
    border-top: 1px solid var(--border);
}

#chat-input {
    flex: 1;
    margin: 0;
}

#chat-send {
    flex: 0 0 auto;
    width: 44px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

@media (max-width: 640px) {
    #chat-panel {
        width: 100vw;
        /* dvh = alto realmente visible: con 100vh el navegador móvil deja el
           formulario de envío oculto tras su barra inferior. */
        height: 100vh;
        height: 100dvh;
        max-width: 100vw;
        max-height: 100vh;
        max-height: 100dvh;
        bottom: 0;
        right: 0;
        border-radius: 0;
    }
}

/* ── Markdown dentro de los mensajes del bot ── */

.chat-msg-bot p { margin: 0 0 0.5rem; }
.chat-msg-bot p:last-child { margin-bottom: 0; }
.chat-msg-bot h1, .chat-msg-bot h2, .chat-msg-bot h3,
.chat-msg-bot h4, .chat-msg-bot h5, .chat-msg-bot h6 {
    margin: 0.4rem 0 0.3rem;
    font-size: 0.95rem;
    font-weight: 600;
}
.chat-msg-bot ul, .chat-msg-bot ol {
    margin: 0.25rem 0 0.5rem;
    padding-left: 1.25rem;
}
.chat-msg-bot li { margin: 0.15rem 0; }
.chat-msg-bot a {
    color: var(--accent);
    text-decoration: underline;
}
.chat-msg-bot code {
    background: rgba(var(--accent-rgb), 0.08);
    padding: 0.05rem 0.3rem;
    border-radius: 4px;
    font-size: 0.85em;
}
.chat-msg-bot pre {
    background: rgba(var(--accent-rgb), 0.08);
    padding: 0.5rem 0.65rem;
    border-radius: 8px;
    overflow-x: auto;
    margin: 0.4rem 0;
}
.chat-msg-bot pre code {
    background: none;
    padding: 0;
}

/* ── IA · Chatea con tus recetas (chat a página completa) ── */

.reciachat-toolbar {
    margin-top: 0.75rem;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

#reciachat-recargar {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    width: auto;
    -webkit-tap-highlight-color: transparent;
}

#reciachat-recargar:disabled {
    cursor: not-allowed;
}

/* En táctil, el botón se quedaba con el color de :hover «pegado» tras pulsarlo (un tono
   distinto al de reposo: parecía tener «doble color»). Sin puntero fino, anulamos el
   cambio de fondo por hover para que conserve su color de reposo. */
@media (hover: none) {
    #reciachat-recargar:hover {
        background: var(--accent-soft);
    }
}

.reciachat-toolbar-hint {
    font-size: 0.8rem;
    color: var(--text-secondary);
}

.reciachat-card {
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow: hidden;
    /* dvh evita que la barra dinámica del navegador móvil sobreestime el alto. */
    height: calc(100vh - 300px);
    height: calc(100dvh - 300px);
    min-height: 380px;
}

.reciachat-messages {
    flex: 1;
    overflow-y: auto;
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

/* Chat a página completa (tablet/escritorio): las burbujas aprovechan casi todo el
   ancho de la tarjeta (antes se acotaban a ~60ch y quedaban perdidas en el hueco). */
.reciachat-messages .chat-msg {
    max-width: 92%;
}

.reciachat-form {
    display: flex;
    gap: 0.5rem;
    padding: 0.75rem;
    border-top: 1px solid var(--border);
}

#reciachat-input {
    flex: 1;
    margin: 0;
}

#reciachat-send {
    flex: 0 0 auto;
    width: 44px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ── Modal de Ajustes ── */

.ajustes-cuenta-resumen {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    padding: 0.9rem 1rem;
    margin-bottom: 1.25rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.ajustes-resumen-label {
    display: block;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--text-secondary);
    margin-bottom: 0.15rem;
}

.ajustes-resumen-valor {
    font-weight: 600;
    color: var(--text);
}

.ajustes-section {
    padding: 1.25rem 0;
    border-top: 1px solid var(--border);
}

.ajustes-section h4 {
    margin-bottom: 0.4rem;
}

.ajustes-hint {
    font-size: 0.82rem;
    color: var(--text-secondary);
    margin: 0 0 0.75rem;
}

.ajustes-aviso-impago {
    font-size: 0.8rem;
    color: #92400e;
    background: #fff6e6;
    border: 1px solid var(--gold);
    border-radius: var(--radius);
    padding: 0.5rem 0.75rem;
    margin: 0 0 0.75rem;
}

/* ── Historial de facturas embebido (Ajustes › Facturación) ──
   Filas compactas: fecha+número | chip de estado | importe | acciones. El chip replica
   la geometría de .sug-estado y sus combinaciones de color AA ya validadas. */
.fact-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.55rem 0;
    border-bottom: 1px solid var(--border);
    font-size: 0.88rem;
}
.fact-item:last-child { border-bottom: none; }
.fact-datos {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
}
.fact-fecha { color: var(--text); }
.fact-numero {
    font-size: 0.75rem;
    color: var(--text-secondary);
}
.fact-estado {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    padding: 0.12rem 0.5rem;
    border-radius: var(--radius-pill);
    background: #eef0f3;
    color: #5b6470;
    white-space: nowrap;
}
.fact-estado-pagada    { background: #E9F2EB; color: #256B41; }
.fact-estado-pendiente { background: #fff6e6; color: #92400e; }
.fact-estado-impagada  { background: #F2E9E6; color: #96432F; }
.fact-importe {
    font-weight: 600;
    white-space: nowrap;
}
.fact-acciones {
    display: flex;
    gap: 0.6rem;
}
.fact-accion {
    font-size: 0.82rem;
    font-weight: 600;
    white-space: nowrap;
}
.fact-aviso-pendiente { margin: 0.6rem 0 0; }

/* Formulario de datos de facturación (checkbox «empresa» reutiliza .auth-consent). */
#form-datos-fact .auth-consent { margin: 0 0 0.75rem; }
#fact-campos-empresa { margin-top: 0.25rem; }
#fact-pais { margin: 0.4rem 0 0; }

/* ── Checkout embebido (vista de pago en el propio dominio) ── */
#checkout-view {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: safe center;
    width: 100vw;
    min-height: 100vh;
    overflow-y: auto;
    padding: 2rem 1rem;
    background: #2F3B4A;
}

.checkout-box {
    width: 100%;
    max-width: 460px;
    background: #fff;
    border-radius: 16px;
    padding: 2rem 1.75rem;
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.18);
    text-align: center;
}

.checkout-title {
    margin: 0 0 0.3rem;
    font-size: 1.35rem;
    color: var(--text);
}

.checkout-sub {
    margin: 0 0 1.25rem;
    font-size: 0.85rem;
    color: var(--text-secondary);
}

.checkout-error {
    background: #fff6e6;
    border: 1px solid var(--gold);
    color: #92400e;
    border-radius: 8px;
    padding: 0.6rem 0.8rem;
    font-size: 0.85rem;
    margin-bottom: 1rem;
}

#checkout-volver {
    margin-top: 1rem;
}

/* ── Aviso de pago pendiente (dunning) ── */
#dunning-banner, #trial-banner {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    background: #fff6e6;
    border-bottom: 1px solid var(--gold);
    color: #92400e;
    padding: 0.6rem 1rem;
    font-size: 0.85rem;
    text-align: center;
}

/* Banner de prueba gratuita: informativo (no es una alerta), tono verde de la marca.
   Texto y botón en la MISMA fila (nowrap): el botón «Suscribirme» ya no baja a una línea
   propia comiéndose espacio; el texto encoge/parte y el botón queda a su derecha. */
#trial-banner {
    background: #E9F2EB;
    border-bottom-color: var(--success, #2f7a50);
    color: #2E5D3B;
    flex-wrap: nowrap;
    gap: 0.5rem 0.75rem;
}

#trial-banner #trial-banner-text {
    min-width: 0;
}

#trial-banner .dunning-btn {
    flex: 0 0 auto;
    white-space: nowrap;
}

.dunning-btn {
    background: var(--accent-soft);
    color: var(--on-accent);
    border: none;
    border-radius: var(--radius-pill);
    padding: 0.35rem 0.9rem;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
}

.dunning-btn:hover {
    background: var(--accent-hover);
}

.ajustes-section form button {
    margin-top: 0.5rem;
    width: auto;
}

/* Formulario embebido de tarjeta (Stripe Elements) en Facturación. */
.ajustes-tarjeta-form {
    margin-top: 0.75rem;
}

#ajustes-card-element {
    padding: 0.85rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
}

.ajustes-tarjeta-form-actions {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.75rem;
}

.ajustes-tarjeta-form-actions button {
    flex: 1;
    width: auto;
    margin-top: 0;
}

/* "Eliminar tarjeta": a ancho completo para encuadrarlo con los demás botones del
   panel (el .btn-danger-ghost es auto-ancho por defecto, para los botones de tabla). */
#btn-ajustes-eliminar-tarjeta {
    width: 100%;
    margin-top: 0.5rem;
}

.ajustes-links {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.ajustes-links a {
    color: var(--accent);
    text-decoration: underline;
}

.ajustes-peligro h4 {
    color: var(--danger);
}

/* ── Ajustes: menú lateral de categorías + paneles ── */

.ajustes-layout {
    display: flex;
    gap: 1.5rem;
    align-items: flex-start;
}

/* "Volver" del drill-down móvil: oculto por defecto (en escritorio nunca se muestra;
   solo aparece en la vista de detalle en móvil, ver media query de abajo). */
.ajustes-back {
    display: none;
}

.ajustes-nav {
    flex: 0 0 180px;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    position: sticky;
    top: 0;
}

.ajustes-nav-btn {
    width: 100%;
    text-align: left;
    background: none;
    color: var(--text-secondary);
    border: none;
    border-radius: var(--radius);
    padding: 0.6rem 0.85rem;
    font-size: 0.9rem;
    font-weight: 500;
    margin-top: 0;
}

.ajustes-nav-btn:hover {
    background: var(--bg);
    color: var(--text);
}

.ajustes-nav-btn.is-active {
    background: var(--accent-soft);
    color: var(--on-accent);
    font-weight: 600;
}

.ajustes-panels {
    flex: 1;
    min-width: 0;
}

.ajustes-panel[hidden] {
    display: none;
}

/* La primera sección de cada panel no necesita el separador superior. */
.ajustes-panel > .ajustes-section:first-child {
    border-top: none;
    padding-top: 0;
}

/* En móvil el modal de Ajustes scrollea como UN TODO (igual que el resto de modales a
   pantalla completa); nada de scroll anidado por panel. Las categorías son una lista de
   filas a ancho completo (drill-down: al tocar una se ve su detalle y desaparece la lista). */
@media (max-width: 639.98px) {
    .ajustes-layout {
        flex-direction: column;
        /* stretch: los hijos ocupan TODO el ancho. Sin esto heredan align-items:flex-start
           (escritorio) y en columna se encogen al ancho de su contenido. */
        align-items: stretch;
        gap: 1rem;
    }

    /* Categorías como FILAS verticales full-width. */
    .ajustes-nav {
        position: static;
        flex-direction: column;
        gap: 0.5rem;
    }

    .ajustes-nav-btn {
        width: 100%;
        display: flex;
        align-items: center;
        justify-content: space-between;
        white-space: normal;
        background: var(--bg);
        border: 1px solid var(--border);
        padding: 0.9rem 1rem;
        font-size: 1rem;
    }

    /* Chevron a la derecha: indica que la fila abre su detalle. */
    .ajustes-nav-btn::after {
        content: "›";
        color: var(--text-secondary);
        font-size: 1.25rem;
        line-height: 1;
    }

    /* En la lista, la fila "activa" no se pinta en acento (es solo un punto de entrada). */
    .ajustes-nav-btn.is-active {
        background: var(--bg);
        color: var(--text);
        font-weight: 500;
    }

    /* Paneles ocultos en la LISTA; visibles solo en el detalle. Sin scroll propio: fluyen
       y, si son altos, scrollea el modal entero (como los demás). */
    .ajustes-panels {
        display: none;
    }

    /* Vista de DETALLE (una categoría abierta): se oculta la lista y se muestran
       "Volver" + el panel de la categoría. */
    .ajustes-layout.is-detail .ajustes-nav {
        display: none;
    }

    .ajustes-layout.is-detail .ajustes-panels {
        display: block;
    }

    .ajustes-layout.is-detail .ajustes-back {
        display: inline-flex;
        align-items: center;
        align-self: flex-start;
        background: none;
        color: var(--accent);
        border: none;
        padding: 0.25rem 0;
        font-size: 0.95rem;
        font-weight: 600;
    }

    /* Botones de acción uniformes en móvil: ancho completo, alto táctil y bien
       espaciados (antes los de formulario y los danger-ghost quedaban a medio ancho,
       desalineados). El par Guardar/Cancelar de la tarjeta NO entra (no es hijo directo
       de la sección): se queda en su propia fila. */
    .ajustes-section > button,
    .ajustes-section form > button {
        width: 100%;
        min-height: 46px;
        margin-top: 0.85rem;
        padding: 0.7rem 1rem;
        border-radius: var(--radius);
        font-size: 0.95rem;
    }
}

/* ── Inicio: panel de observabilidad ── */

.dash-hero {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.5rem;
}

.dash-hero-text h2 {
    font-size: 1.9rem;
    font-weight: 700;
    color: var(--text);
    margin-bottom: 0.15rem;
}

.dash-hero-sub {
    color: var(--text-secondary);
    font-size: 0.95rem;
}

.dash-toolbar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-bottom: 1.25rem;
}

.dash-refresh-note {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.8rem;
    color: var(--text-secondary);
}

.dash-refresh-note svg {
    flex: 0 0 auto;
    opacity: 0.8;
}

.dash-toolbar-label {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.dash-toolbar .segmented {
    margin-top: 0;
}

/* ── Lanzador de «Primeros pasos» (#onboarding-launcher) ──
   Flotante arriba a la derecha: pastilla clara con la etiqueta y un anillo de progreso que se
   llena de latón (sobre pista marino) según los pasos completados, con el contador X/6 en el
   centro — el patrón marino+latón de la marca, como el logo. En móvil se colapsa a solo el anillo. */
#onboarding-launcher {
    position: fixed;
    top: 14px;
    right: 16px;
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    height: 50px;
    padding: 0 7px 0 1rem;
    border: 1px solid rgba(181, 141, 83, 0.5);
    border-radius: var(--radius-pill);
    background: var(--card-bg);
    color: var(--accent);
    cursor: pointer;
    box-shadow: var(--shadow-lg);
    transition: box-shadow 0.15s, transform 0.15s, border-color 0.15s;
    /* Sobre el topbar móvil (100); por debajo de modales (1000) y del aviso de cookies. */
    z-index: 951;
}

#onboarding-launcher:hover {
    transform: translateY(-1px);
    border-color: var(--gold);
    box-shadow: 0 10px 32px rgba(var(--accent-rgb), 0.18);
}

.onboarding-launcher-label {
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: -0.1px;
    white-space: nowrap;
}

/* Anillo + contador, superpuestos en el mismo cuadro. */
.onboarding-ring-wrap {
    position: relative;
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.onboarding-ring {
    position: absolute;
    inset: 0;
    width: 36px;
    height: 36px;
}

.onboarding-ring-track {
    stroke: rgba(var(--accent-rgb), 0.16);
    stroke-width: 5;
}

.onboarding-ring-progress {
    stroke: var(--gold);
    stroke-width: 5;
    stroke-linecap: round;
    transition: stroke-dashoffset 0.4s ease;
}

.onboarding-ring-count {
    position: relative;
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: -0.3px;
    color: var(--accent);
}

/* En móvil, reserva hueco a la derecha de los banners superiores para que su contenido
   (p. ej. el botón «Suscribirme» del trial) no quede bajo el lanzador flotante, que aquí se
   colapsa a solo el anillo (sin etiqueta) para no robar ancho a la barra superior. */
@media (max-width: 1023.98px) {
    #dunning-banner,
    #trial-banner {
        padding-right: calc(70px + env(safe-area-inset-right));
    }

    #onboarding-launcher {
        gap: 0;
        width: 50px;
        padding: 0;
        justify-content: center;
    }

    .onboarding-launcher-label {
        display: none;
    }
}

/* Celebración al completar los 6 pasos: el anillo (ya lleno) pulsa en latón durante
   los ~3 s que separan el último paso del modal de enhorabuena. */
#onboarding-launcher.onboarding-celebrando {
    border-color: var(--gold);
    animation: onboardingPulso 1s ease-out 3;
}

/* Confeti de la enhorabuena: piezas absolutas dentro de la tarjeta (recortadas por su
   borde), pintadas por JS con deriva/duración aleatorias vía variables inline. */
#modal-enhorabuena .modal-content {
    position: relative;
}

.confeti-capa {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    border-radius: inherit;
}

.confeti-pieza {
    position: absolute;
    top: -14px;
    border-radius: 2px;
    opacity: 0;
    animation: confetiCaida linear forwards;
}

@keyframes confetiCaida {
    0% { transform: translate(0, 0) rotate(0deg); opacity: 1; }
    100% { transform: translate(var(--confeti-deriva, 0px), 480px) rotate(540deg); opacity: 0.3; }
}

/* Modal de enhorabuena: «tarjeta» con el icono de Jarvis (igual que su lanzador). */
.enhorabuena-jarvis {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 1rem 0 0.5rem;
    padding: 0.75rem 0.9rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    font-size: 0.9rem;
}

.enhorabuena-jarvis-icono {
    flex: 0 0 auto;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: var(--accent);
    color: var(--on-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: var(--shadow-md);
}

/* Título de la guía de bienvenida (modal). */
.bienvenida-titulo {
    margin-bottom: 0.35rem;
}

/* Subtítulo de la guía de "Primeros pasos" (modal de bienvenida). */
.dash-onboarding-sub {
    color: var(--text-secondary);
    font-size: 0.9rem;
    margin-bottom: 0.85rem;
}

/* Barra de progreso del modal: se llena de latón (como el anillo del lanzador),
   con el contador «X de 6» al lado. */
.onboarding-progreso {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 1rem;
}

.onboarding-progreso-barra {
    flex: 1;
    height: 8px;
    border-radius: var(--radius-pill);
    background: var(--panel);
    overflow: hidden;
}

.onboarding-progreso-fill {
    height: 100%;
    width: 0;
    border-radius: var(--radius-pill);
    background: var(--gold);
    transition: width 0.4s ease;
}

.onboarding-progreso-texto {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--gold-text);
    white-space: nowrap;
}

/* ── «Camino conectado» ──
   Cada paso es una fila transparente (marcador circular + texto); una línea vertical
   por segmentos (li::before) une los marcadores y se tiñe de verde en los pasos ya
   cumplidos. El marcador tapa la línea (position relative + fondo opaco). */
.onboarding-list {
    list-style: none;
    display: flex;
    flex-direction: column;
}

.onboarding-list li {
    position: relative;
    padding: 0.2rem 0;
}

.onboarding-list li::before {
    content: "";
    position: absolute;
    /* Centro del marcador: 6px de padding del botón + 13px de radio - 1px (mitad del ancho). */
    left: 18px;
    top: 0;
    bottom: 0;
    width: 2px;
    background: var(--border);
}

.onboarding-list li:first-child::before {
    top: 50%;
}

.onboarding-list li:last-child::before {
    bottom: 50%;
}

/* Sin :has() la línea queda gris entera: los marcadores ya cuentan el estado. */
.onboarding-list li:has(.onboarding-done)::before {
    background: var(--green);
}

.onboarding-step {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    width: 100%;
    text-align: left;
    padding: 0.3rem 0.4rem 0.3rem 6px;
    border: none;
    border-radius: var(--radius);
    background: transparent;
    color: var(--text);
    font-size: 0.95rem;
    cursor: pointer;
    transition: background 0.15s ease;
}

.onboarding-step:hover {
    background: var(--bg);
}

.onboarding-marker {
    position: relative;
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--panel);
    border: 1px solid var(--border);
    color: var(--text-secondary);
    font-size: 0.8rem;
    font-weight: 600;
}

.onboarding-num {
    line-height: 1;
}

.onboarding-check {
    position: absolute;
    width: 15px;
    height: 15px;
    opacity: 0;
}

/* El check se "dibuja" (trazo SVG) al marcarse el paso; reduced-motion ya lo anula. */
.onboarding-check path {
    stroke-dasharray: 21;
    stroke-dashoffset: 21;
}

.onboarding-texto {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

/* Paso cumplido: marcador verde con check dibujado y texto atenuado. */
.onboarding-step.onboarding-done .onboarding-marker {
    background: var(--green);
    border-color: var(--green);
    color: #fff;
}

.onboarding-step.onboarding-done .onboarding-num {
    visibility: hidden;
}

.onboarding-step.onboarding-done .onboarding-check {
    opacity: 1;
}

.onboarding-step.onboarding-done .onboarding-check path {
    stroke-dashoffset: 0;
    transition: stroke-dashoffset 0.45s ease 0.1s;
}

.onboarding-step.onboarding-done .onboarding-texto {
    color: var(--text-secondary);
}

/* Primer paso pendiente («Siguiente»): marcador latón con halo pulsante y tarjeta
   resaltada con chip, para responder «¿y ahora qué?» de un vistazo. */
.onboarding-step.onboarding-siguiente .onboarding-marker {
    background: var(--card-bg);
    border: 2px solid var(--gold);
    color: var(--gold-text);
    animation: onboardingPulso 2s ease-out infinite;
}

@keyframes onboardingPulso {
    0% { box-shadow: 0 0 0 0 rgba(181, 141, 83, 0.45); }
    70% { box-shadow: 0 0 0 8px rgba(181, 141, 83, 0); }
    100% { box-shadow: 0 0 0 0 rgba(181, 141, 83, 0); }
}

.onboarding-step.onboarding-siguiente .onboarding-texto {
    background: rgba(181, 141, 83, 0.08);
    border: 1px solid var(--gold);
    border-radius: var(--radius);
    padding: 0.5rem 0.75rem;
}

.onboarding-step.onboarding-siguiente .onboarding-texto::after {
    content: "Siguiente";
    flex: 0 0 auto;
    margin-left: auto;
    font-size: var(--fs-xs);
    font-weight: 600;
    padding: 0.1rem 0.6rem;
    border-radius: var(--radius-pill);
    background: var(--gold-text);
    color: var(--on-accent);
}

.onboarding-step.onboarding-siguiente:hover .onboarding-texto {
    border-color: var(--gold-hover);
}

/* KPIs */
.dash-kpis {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 1rem;
    margin-bottom: 1.25rem;
}

.dash-kpi {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.1rem 1.25rem;
    box-shadow: var(--shadow-sm);
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.dash-kpi-label {
    font-size: 0.8rem;
    color: var(--text-secondary);
    font-weight: 500;
}

.dash-kpi-label small {
    font-size: 0.72rem;
    opacity: 0.8;
}

.dash-kpi-value {
    font-size: 1.55rem;
    font-weight: 700;
    color: var(--text);
    line-height: 1.1;
}

.dash-kpi-facturado {
    background: var(--accent);
    border-color: var(--accent);
}

.dash-kpi-facturado .dash-kpi-label,
.dash-kpi-facturado .dash-kpi-value {
    color: var(--on-accent);
}

/* En la tarjeta marino, el "(entregados)" atenuado quedaba corto de contraste. */
.dash-kpi-facturado .dash-kpi-label small {
    font-size: 0.75rem;
    opacity: 1;
}

/* Rejilla de gráficos */
.dash-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1.25rem;
    align-items: start;
}

.dash-chart-card {
    margin-bottom: 0;
}

.dash-chart-card h3 small {
    font-weight: 400;
    color: var(--text-secondary);
    font-size: 0.8rem;
}

.dash-chart-wide {
    grid-column: span 2;
}

.dash-canvas-wrap {
    position: relative;
    height: 260px;
}

/* Sin datos en el periodo: el canvas está oculto y la tarjeta se compacta (sin
   esto quedaban ~260px de hueco con una sola línea de texto centrada). */
.dash-canvas-wrap.dash-canvas-vacio {
    height: auto;
}

.dash-empty {
    color: var(--text-secondary);
    font-size: 0.88rem;
    text-align: center;
    padding: 2rem 1rem;
}

/* Tablet: una sola columna de gráficos. */
@media (max-width: 1023.98px) {
    .dash-grid {
        grid-template-columns: 1fr;
    }

    .dash-chart-wide {
        grid-column: span 1;
    }

    .dash-kpis {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 639.98px) {
    .dash-hero h2 {
        font-size: 1.5rem;
    }

    .dash-refresh-note {
        margin-left: 0;
    }

    .dash-canvas-wrap {
        height: 230px;
    }
}

/* ── Calendario de entregas ── */
.cal-toolbar {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 1rem;
}

/* Grupo de navegación (‹ Hoy ›): juntos a la derecha, para no tener que cruzar
   todo el toolbar al pasar varios meses. */
.cal-toolbar-nav {
    display: flex;
    gap: 0.4rem;
    flex: 0 0 auto;
}

/* Selector con la especificidad de `.card h3` (0,2,0 > 0,1,1) para poder anular su
   border-bottom/padding, que con flex:1 pintaba una línea a lo ancho del toolbar. */
.cal-toolbar .cal-titulo {
    margin: 0;
    flex: 1;
    color: var(--accent);
    border-bottom: none;
    padding-bottom: 0;
}

/* Los botones del toolbar son .btn-secondary, que por defecto es width:100% +
   margin-top: anularlo aquí para que prev/título/next/Hoy queden en una sola línea. */
.cal-toolbar .btn-secondary {
    width: auto;
    margin-top: 0;
    flex: 0 0 auto;
    white-space: nowrap;
}

.cal-weekdays,
.cal-grid {
    display: grid;
    /* minmax(0, 1fr) y no 1fr: el mínimo automático dejaría que un nombre de
       cliente largo ensanche su columna y el domingo se salga de la pantalla
       en móvil; con mínimo 0 las 7 columnas son siempre iguales y el texto
       se trunca con elipsis dentro de su celda. */
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 6px;
}

.cal-weekdays {
    margin-bottom: 6px;
}

/* Mes sin ninguna entrega: aviso bajo la cuadrícula. */
.cal-vacio {
    margin-top: 0.85rem;
    text-align: center;
    font-size: 0.88rem;
    color: var(--text-secondary);
}

.cal-weekdays span {
    text-align: center;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.cal-cell {
    min-height: 92px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    background: var(--card-bg);
    transition: transform 0.18s ease, box-shadow 0.18s ease;
}

.cal-cell-empty {
    border: none;
    background: transparent;
}

.cal-cell-has {
    cursor: pointer;
    border-color: var(--gold);
}

.cal-cell-has:hover {
    background: rgba(181, 141, 83, 0.08);
}

.cal-cell-today {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* El foco de teclado en las celdas-día debe distinguirse del resaltado permanente
   de «hoy» (mismo outline granate): anillo dorado más grueso y por fuera. */
.cal-cell[role="button"]:focus-visible {
    outline: 3px solid var(--gold);
    outline-offset: 1px;
}

/* Día desplegado: se "aumenta" sobre el resto (zoom + sombra) para que el día y
   sus eventos se vean más grandes. */
.cal-cell-expanded {
    background: rgba(181, 141, 83, 0.12);
    transform: scale(1.12);
    transform-origin: center;
    z-index: 5;
    position: relative;
    border-color: var(--accent);
    box-shadow: var(--shadow-lg);
}

.cal-cell-expanded .cal-daynum {
    font-size: 1rem;
}

.cal-cell-expanded .cal-evento {
    font-size: 0.85rem;
    padding: 5px 8px;
}

/* En el día desplegado las pills sustituyen al badge "N entregas" (en móvil es
   al revés: ver el bloque responsive del calendario). */
.cal-cell-expanded .cal-badge {
    display: none;
}

.cal-evento-hora {
    font-weight: 600;
}

/* Panel de detalle del día (solo móvil): bajo la cuadrícula, con los nombres
   completos y la hora; dentro de una celda de ~45px solo cabían "C…", "B…". */
#cal-detalle {
    display: none;
}

.cal-daynum {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-secondary);
}

.cal-evento-uno {
    font-size: 0.78rem;
    color: var(--text);
    background: rgba(71, 112, 79, 0.16);
    border-radius: 6px;
    padding: 2px 6px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cal-badge {
    font-size: 0.76rem;
    font-weight: 600;
    color: var(--on-accent);
    background: var(--accent);
    border-radius: var(--radius-pill);
    padding: 2px 8px;
    align-self: flex-start;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cal-evento {
    display: block;
    width: 100%;
    text-align: left;
    font-size: 0.76rem;
    color: var(--text);
    background: rgba(71, 112, 79, 0.16);
    border: none;
    border-radius: 6px;
    padding: 3px 6px;
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cal-evento:hover {
    background: rgba(71, 112, 79, 0.3);
}

@media (max-width: 639.98px) {
    .cal-cell {
        min-height: 64px;
        padding: 4px;
    }

    /* gap de 6px × 6 huecos consumía ~36px de un ancho ya escaso: celdas más anchas. */
    .cal-weekdays,
    .cal-grid {
        gap: 3px;
    }

    .cal-daynum {
        font-size: 0.72rem;
    }

    .cal-evento-uno,
    .cal-evento,
    .cal-badge {
        font-size: 0.68rem;
    }

    /* En celdas estrechas se muestra el CLIENTE truncado y se oculta la hora (decisión
       del dueño 2026-07-14, revierte la auditoría 2026-07 que mostraba la hora): el
       nombre es lo que identifica la entrega de un vistazo; la hora queda en el detalle
       del pedido. El badge de varias entregas queda solo con el número. */
    .cal-evento-uno {
        background: none;
        padding: 0;
        display: flex;
        align-items: center;
        gap: 3px;
    }

    .cal-evento-uno .cal-evento-hora {
        display: none;
    }

    .cal-evento-uno .cal-evento-nombre {
        font-size: 0.72rem;
        font-weight: 600;
        color: var(--green);
        white-space: nowrap;
        /* min-width 0: sin él, un flex-item no encoge y el ellipsis no llega a actuar. */
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .cal-badge-sufijo {
        display: none;
    }

    /* En móvil el detalle del día desplegado vive en el panel bajo la cuadrícula:
       dentro de la celda se mantiene el badge con el número y se ocultan las
       pills (truncadas a una letra no aportaban nada). */
    .cal-cell-expanded .cal-evento {
        display: none;
    }

    .cal-cell-expanded .cal-badge {
        display: inline-block;
    }

}

/* Panel de detalle del día desplegado: también en TABLET (640-1023). Las pills dentro
   de una celda de ~100px seguían truncando nombres; el panel bajo la cuadrícula los
   muestra completos con su hora (en tablet conviven pills y panel). */
@media (max-width: 1023.98px) {
    #cal-detalle:not(.hidden) {
        display: block;
        margin-top: 1rem;
        padding: 0.85rem;
        background: var(--bg);
        border: 1px solid var(--gold);
        border-radius: var(--radius);
    }

    #cal-detalle h4 {
        margin: 0 0 0.6rem;
        font-size: 0.9rem;
        color: var(--accent);
    }

    .cal-detalle-evento {
        display: block;
        width: 100%;
        text-align: left;
        font-size: 0.9rem;
        color: var(--text);
        background: rgba(71, 112, 79, 0.16);
        border: none;
        border-radius: 8px;
        padding: 0.6rem 0.75rem;
        margin-bottom: 0.4rem;
        cursor: pointer;
    }

    .cal-detalle-evento:last-child {
        margin-bottom: 0;
    }
}

/* ── Legal: footer del login, consentimiento y contenido de los textos ── */
.auth-legal {
    margin-top: 1.5rem;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.25rem 1rem;
}

.auth-legal a {
    color: rgba(255, 255, 255, 0.75);
    font-size: 0.78rem;
    text-decoration: underline;
}

.auth-consent {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    text-align: left;
    font-size: 0.82rem;
    color: var(--text-secondary);
}

.auth-consent input {
    width: auto;
    margin-top: 0.2rem;
    flex-shrink: 0;
}

.auth-consent a {
    color: var(--accent);
}

/* Consentimiento sin marcar al enviar: se resalta en rojo (en vez del bocadillo
   nativo del navegador, que salía en inglés). */
.auth-consent.consent-error {
    color: var(--danger);
}

.auth-consent.consent-error input {
    outline: 2px solid var(--danger);
    outline-offset: 2px;
}

/* Checkbox «compro como empresa o autónomo» sobre el checkout embebido (reactivación
   y modal de conversión): reutiliza .auth-consent; solo necesita aire hacia abajo. */
.checkout-empresa {
    margin: 0 0 0.9rem;
}

/* Campo de código de promoción PROPIO (reactivación y modal de conversión): input +
   «Aplicar» en línea. El uppercase es solo visual: la normalización real la hace el
   backend (el canje de Stripe es case-insensitive). */
.checkout-codigo {
    display: flex;
    gap: 0.5rem;
    margin: 0 0 0.9rem;
}
.checkout-codigo input {
    flex: 1;
    min-width: 0;
    text-transform: uppercase;
    /* El margen global de los inputs desalineaba la fila (el botón se estiraba 1rem
       más alto que el campo); dentro de la fila flex el aire lo pone el gap. */
    margin-bottom: 0;
}

/* Botón "Crear Cuenta" durante su cooldown anti-spam: atenuado pero SÍ clicable
   (al pulsarlo avisa de los segundos que faltan), así que conserva el cursor de
   botón — not-allowed mentía sobre la interacción real. */
.btn-cooldown {
    opacity: 0.6;
}

.legal-body {
    text-align: left;
    color: var(--text);
    font-size: 0.9rem;
    line-height: 1.5;
}

.legal-body h4 {
    margin: 1.2rem 0 0.4rem;
    color: var(--text);
}

.legal-body ul {
    margin: 0 0 0.6rem 1.25rem;
}

.legal-body a {
    color: var(--accent);
}

/* ── Guía de uso (modal-ayuda): acordeón de secciones <details> ── */
.ayuda-body {
    text-align: left;
    color: var(--text);
    font-size: 0.9rem;
    line-height: 1.55;
}

.ayuda-intro {
    color: var(--text-secondary);
    margin-bottom: 1rem;
}

.ayuda-body details {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin-bottom: 0.6rem;
}

.ayuda-body summary {
    cursor: pointer;
    padding: 0.7rem 0.9rem;
    font-weight: 600;
    color: var(--accent);
    list-style: none;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
}

.ayuda-body summary::-webkit-details-marker {
    display: none;
}

.ayuda-body summary::after {
    content: '+';
    font-size: 1.2rem;
    line-height: 1;
    color: var(--gold-text);
}

.ayuda-body details[open] summary::after {
    content: '\2013';
}

.ayuda-body details[open] summary {
    border-bottom: 1px solid var(--border);
}

.ayuda-body details > *:not(summary) {
    padding-left: 0.9rem;
    padding-right: 0.9rem;
}

.ayuda-body details > *:nth-child(2) {
    margin-top: 0.7rem;
}

.ayuda-body details > *:last-child {
    padding-bottom: 0.8rem;
}

.ayuda-body p {
    margin: 0.5rem 0;
}

.ayuda-body ul {
    margin: 0.4rem 0 0.4rem 1.25rem;
}

.ayuda-body li {
    margin-bottom: 0.3rem;
}

/* ── Micro-encuesta NPS (#nps-card) ── */
.nps-card {
    position: fixed;
    bottom: 1.25rem;
    right: 1.25rem;
    z-index: 900; /* por debajo de modales (1000): un modal abierto la tapa */
    width: 360px;
    max-width: calc(100vw - 2rem);
    /* En apaisado o con el teclado abierto (comentario desplegado) la tarjeta no debe
       recortarse: se acota al alto visible y scrollea por dentro si hace falta. */
    max-height: calc(100dvh - 2rem);
    overflow-y: auto;
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 1.25rem;
    animation: nps-in 0.35s ease both;
}

@keyframes nps-in {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: translateY(0); }
}

.nps-close {
    position: absolute;
    top: 0.5rem;
    right: 0.6rem;
    border: none;
    background: none;
    font-size: 1.4rem;
    line-height: 1;
    color: var(--text-secondary);
    cursor: pointer;
    padding: 0.2rem;
}

.nps-close:hover {
    color: var(--text);
}

.nps-q {
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--text);
    margin: 0 1.2rem 0.9rem 0;
    line-height: 1.4;
}

.nps-scale {
    display: flex;
    gap: 0.3rem;
}

.nps-num {
    flex: 1 1 0;
    min-width: 0;
    padding: 0.4rem 0;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: var(--card-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 0.82rem;
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.nps-num:hover {
    border-color: var(--accent);
}

.nps-num.sel {
    background: var(--accent-soft);
    color: var(--on-accent);
    border-color: var(--accent-soft);
}

.nps-scale-labels {
    display: flex;
    justify-content: space-between;
    margin-top: 0.4rem;
    font-size: 0.72rem;
    color: var(--text-secondary);
}

.nps-comentario-wrap {
    margin-top: 0.85rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.nps-comentario-wrap textarea {
    width: 100%;
    font-family: inherit;
    font-size: 0.88rem;
    padding: 0.5rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    resize: vertical;
}

.nps-comentario-wrap textarea:focus {
    outline: none;
    border-color: var(--accent);
}

.nps-comentario-wrap .btn-success {
    align-self: flex-end;
}

/* Fallo de envío de la encuesta (antes era silencioso). */
.nps-error {
    margin: 0.4rem 0 0;
    font-size: 0.85rem;
    color: var(--danger);
    text-align: right;
}

.nps-gracias {
    font-size: 0.95rem;
    color: var(--text);
    text-align: center;
    padding: 0.6rem 0;
    margin: 0;
}

@media (max-width: 600px) {
    /* Centrada vertical y horizontalmente: en el borde inferior, el teclado del móvil la
       tapaba al escribir el comentario. La animación de entrada se redefine para no
       pelearse con el translate de centrado. */
    .nps-card {
        left: 1rem;
        right: 1rem;
        top: 50%;
        bottom: auto;
        width: auto;
        transform: translateY(-50%);
        animation: nps-in-center 0.35s ease both;
    }

    @keyframes nps-in-center {
        from { opacity: 0; transform: translate(0, calc(-50% + 12px)); }
        to   { opacity: 1; transform: translateY(-50%); }
    }
}

/* ── Banner de cookies ── */
#cookie-banner {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* Por encima del lanzador del asistente (950) y de los modales (1000-1100):
       su botón "Entendido" debe poder pulsarse siempre. */
    z-index: 1300;
    background: var(--card-bg);
    border-top: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
}

.cookie-banner-inner {
    max-width: 960px;
    margin: 0 auto;
    padding: 1rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.cookie-text {
    font-size: 0.85rem;
    color: var(--text);
    margin: 0;
}

.cookie-text a {
    color: var(--accent);
}

.cookie-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: flex-end;
}

@media (min-width: 640px) {
    .cookie-banner-inner {
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
    }

    .cookie-text {
        flex: 1;
    }
}

/* El aviso de validación puede listar varios campos, uno por línea. */
#modal-alert-message {
    white-space: pre-line;
}

/* Chevron del combobox: una flecha a la derecha que comunica que es desplegable
   (antes parecía un campo de texto libre hasta enfocarlo). */
.combobox input {
    padding-right: 2rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%236B6459' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.65rem center;
    background-size: 1rem;
}

/* El combobox del selector de ingrediente vive en una fila flex: sin margen inferior
   (lo trae .combobox por defecto, pensado para formularios apilados). */
.ingrediente-row .combobox {
    margin-bottom: 0;
    flex: 1;
}

/* Aviso de error de carga del panel de Inicio (no confundir con cuenta vacía). */
.dash-error {
    border-left: 4px solid var(--danger);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.dash-error p {
    margin: 0;
    color: var(--text);
}

.dash-error .btn-secondary {
    width: auto;
    margin-top: 0;
    border-style: solid;
    flex: 0 0 auto;
}

/* Toast con acción "Deshacer". */
.toast-undo {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    background: var(--accent);
}

.toast-undo-btn {
    background: rgba(255, 255, 255, 0.18);
    color: var(--on-accent);
    border: 1px solid rgba(255, 255, 255, 0.5);
    padding: 0.25rem 0.7rem;
    font-size: 0.82rem;
    font-weight: 600;
    border-radius: 6px;
    flex: 0 0 auto;
    min-height: 0;
}

.toast-undo-btn:hover {
    background: rgba(255, 255, 255, 0.3);
}

/* Clientes archivados: fila atenuada + badge. */
tr.row-archivada {
    opacity: 0.65;
}

.cliente-archivado-badge {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.1rem 0.45rem;
    border-radius: var(--radius-pill);
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--text-secondary);
    border: 1px solid var(--border);
    vertical-align: middle;
}

.cli-archivados-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    color: var(--text-secondary);
    white-space: nowrap;
    margin: 0;
}

.cli-archivados-toggle input {
    width: auto;
    margin: 0;
}

/* ── Impresión: una hoja limpia del modal abierto (pedido / receta), sin la app
   alrededor. Útil para llevar el pedido al obrador en papel (Ctrl+P). ── */
@media print {
    .sidebar,
    .mobile-topbar,
    #chat-launcher,
    #chat-panel,
    #onboarding-launcher,
    #cookie-banner,
    #toast-container,
    .modal-actions {
        display: none !important;
    }

    /* Si hay un modal abierto, se imprime solo su contenido a página completa. */
    body, #app-view, .main-content {
        display: block !important;
        height: auto !important;
        overflow: visible !important;
        background: #fff !important;
    }

    .modal:not(.hidden) {
        position: static !important;
        display: block !important;
        background: none !important;
    }

    .modal:not(.hidden) .modal-content {
        box-shadow: none !important;
        max-height: none !important;
        width: 100% !important;
        max-width: 100% !important;
        overflow: visible !important;
    }
}

/* ── Accesibilidad: respetar la preferencia de menos movimiento ──
   Duración ~0 en vez de `none`: los transitionend de los que depende el código
   (p. ej. retirar el splash tras su fundido) siguen disparándose. */
@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ── Sugerencias (Ajustes › Sugerencias): histórico de solo lectura ── */
.sugerencias-lista {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-top: 0.75rem;
}
.sugerencias-vacio {
    color: var(--text-secondary);
    font-size: 0.9rem;
    line-height: 1.5;
}
.sug-item {
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.75rem 0.9rem;
    background: var(--card-bg);
}
.sug-cab {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.4rem;
}
.sug-estado {
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    padding: 0.12rem 0.5rem;
    border-radius: var(--radius-pill);
    background: #eef0f3;
    color: #5b6470;
    white-space: nowrap;
}
.sug-estado-nueva      { background: #E8EFF6; color: #33608D; }
/* Oscurecidos para cumplir AA (antes 3,9:1 y 4,4:1 sobre sus fondos pastel). */
.sug-estado-resuelta   { background: #E9F2EB; color: #256B41; }
.sug-estado-descartada { background: #F2E9E6; color: #96432F; }
.sug-estado-leida      { background: #eef0f3; color: #5b6470; }
.sug-fecha {
    font-size: 0.78rem;
    color: var(--text-secondary);
}
.sug-mensaje {
    font-size: 0.92rem;
    white-space: pre-wrap;
    word-break: break-word;
}
.sug-respuesta {
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px dashed var(--border);
    font-size: 0.9rem;
    white-space: pre-wrap;
    word-break: break-word;
}
.sug-respuesta-label {
    font-weight: 600;
}
.sug-meta {
    font-size: 0.78rem;
    color: var(--text-secondary);
    margin-bottom: 0.35rem;
}
/* Formulario de envío de sugerencia (Ajustes › Sugerencias) */
.sug-form {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-top: 0.5rem;
}
.sug-form-fila {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}
.sug-form-campo {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.82rem;
    color: var(--text-secondary);
    flex: 1 1 160px;
}
.sug-form textarea {
    width: 100%;
    resize: vertical;
    font: inherit;
}

/* ══════════════════════════════════════════════════════════════════════════
   AUDITORÍA MÓVIL / TABLET (2026-07) — correcciones transversales.
   Bloque al final a propósito: sobreescribe por cascada sin tocar reglas previas.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Red de seguridad anti-scroll-horizontal ─────────────────────────────── */
html { overflow-x: hidden; }

/* Overlays a 100vw incluían el ancho de la scrollbar → franja/scroll lateral. */
.modal { width: 100%; }
#auth-view { width: 100%; }
#checkout-view { width: 100%; }

/* ── Ergonomía táctil: mínimo 44px en TODO dispositivo táctil (móvil Y tablet) ─
   any-pointer:coarse cubre el rango 640-1023px que las media queries en px ignoraban
   Y los híbridos (iPad con trackpad, portátiles táctiles) donde el puntero primario
   es fino pero el dedo también se usa. */
@media (any-pointer: coarse) {
    button,
    .btn,
    .btn-small,
    [role="button"] { min-height: 44px; }

    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
    select,
    textarea { min-height: 44px; }

    /* La edición inline y las cantidades no deben provocar zoom en iOS (>=16px). */
    input, select, textarea { font-size: 16px; }

    /* Casilleros: agrandados a diana cómoda sin heredar el min-height de arriba. */
    input[type="checkbox"],
    input[type="radio"] { width: 22px; height: 22px; }

    /* El radio del control segmentado es invisible y cubre TODA la pastilla
       (inset:0): la regla de 22px de arriba lo encogía a una esquina. */
    .segmented-option input[type="radio"] { width: 100%; height: 100%; }

    /* La pastilla completa del segmented alcanza la diana táctil. */
    .segmented-option span {
        min-height: 44px;
        display: flex;
        align-items: center;
    }

    /* Botón hamburguesa: es el control de navegación primario. */
    .mobile-menu-btn { min-width: 44px; min-height: 44px; }

    /* Acciones de fila (editar / duplicar / borrar / estado): dianas cuadradas. */
    .acciones-cell button,
    .acciones-inner button,
    .acciones-cell .btn-small { min-width: 44px; min-height: 44px; }
    .acciones-inner { gap: 8px; }
    .acciones-cell svg,
    .acciones-inner svg { width: 20px; height: 20px; }

    /* Quitar ingrediente/receta (acción destructiva del flujo núcleo): la regla
       genérica solo daba min-height y el ancho se quedaba en 36px. */
    .ingrediente-row button {
        min-width: 44px;
        width: 44px;
        height: 44px;
    }

    /* Opciones del combobox (elegir la materia prima, el control más usado). */
    .combobox-list li {
        min-height: 44px;
        display: flex;
        align-items: center;
    }

    /* Acordeones de la Guía de uso. */
    .ayuda-body summary { min-height: 44px; }

    /* «Deshacer» del toast: única vía de revertir un borrado; su min-height:0
       base lo dejaba en ~27px. */
    .toast-undo-btn { min-height: 44px; padding-inline: 0.9rem; }

    /* Mostrar/ocultar contraseña, cierres de chat/encuesta, paginación, meses. */
    .pass-toggle { min-width: 44px; min-height: 44px; }
    #chat-close, .nps-close, .modal-close-x { min-width: 44px; min-height: 44px; }
    #chat-close { display: flex; align-items: center; justify-content: center; }
    .pagination .btn-small, .cal-toolbar-nav button { min-width: 44px; min-height: 44px; }
}

/* ── Feedback de pulsación (no existía ni una regla :active) ──────────────── */
button, a, [role="button"], .nav-btn, tr[data-action] {
    -webkit-tap-highlight-color: rgba(var(--accent-rgb), 0.14);
}
button:active,
.btn-small:active,
[role="button"]:active,
.nav-btn:active { transform: scale(0.97); }
tr.row-clickable:active,
tr[data-action]:active { background: rgba(var(--accent-rgb), 0.08); }
/* Los enlaces de texto también confirman la pulsación (los botones ya escalan). */
a:active { opacity: 0.7; }
/* Las celdas-día del calendario son divs [data-action] sin cobertura del bloque de
   arriba (no son tr): mismo feedback al toque. */
.cal-cell-has:active { background: rgba(181, 141, 83, 0.18); }

/* En táctil el :hover se queda "pegado" tras tocar: se re-fija cada familia de botón
   a su color de REPOSO. Ojo: `background: initial` (= transparente) solo vale para
   los que ya son transparentes en reposo (.nav-btn); en un botón de marca dejaba el
   texto claro sobre nada tras el toque. */
@media (hover: none) {
    .btn-danger-ghost:hover { background: transparent; color: var(--danger); }
    tr.row-clickable:hover { background: transparent; }
    button:hover { background: var(--accent); }
    .btn-secondary:hover { background: transparent; }
    .btn-cancel:hover { background: transparent; color: var(--text-secondary); }
    .btn-success:hover { background: var(--green); }
    .btn-gold:hover { background: var(--gold-text); }
    .btn-danger:hover { background: var(--danger); }
    .dunning-btn:hover { background: var(--accent-soft); }
    .pass-toggle:hover { background: none; }
    .toast-undo-btn:hover { background: rgba(255, 255, 255, 0.18); }
    button.badge-rota:hover { background: #fef3c7; }
    .cal-evento:hover { background: rgba(71, 112, 79, 0.16); }
    .cal-detalle-evento:hover { background: rgba(71, 112, 79, 0.16); }
    .cal-cell-has:hover { background: var(--card-bg); }
    .onboarding-step:hover { background: transparent; }
    .onboarding-step.onboarding-siguiente:hover .onboarding-texto { border-color: var(--gold); }
    .ajustes-nav-btn:hover { background: transparent; color: var(--text-secondary); }
    .ajustes-nav-btn.is-active:hover { background: var(--accent-soft); color: var(--on-accent); }
    .acciones-cell button:hover { box-shadow: none; }
    .nav-btn:hover { background: initial; }
    nav button:hover { background: transparent; color: var(--sidebar-text); }
    nav button.active:hover { background: var(--sidebar-active); color: var(--on-accent); }
    #logout-btn:hover { background: transparent; color: var(--sidebar-text); }
    .ingrediente-row button:hover { background: var(--danger); }
    .modal-close-x:hover { background: transparent; color: var(--text-secondary); }
    #onboarding-launcher:hover {
        transform: none;
        border-color: rgba(181, 141, 83, 0.5);
        box-shadow: var(--shadow-lg);
    }
}

/* ── Safe-area (notch / isla dinámica / barra de gestos) ──────────────────────
   Requiere viewport-fit=cover (añadido en index.html). */
.mobile-topbar {
    padding-top: calc(0.75rem + env(safe-area-inset-top));
    padding-left: calc(1rem + env(safe-area-inset-left));
    padding-right: calc(1rem + env(safe-area-inset-right));
}
#cookie-banner { padding-bottom: calc(1rem + env(safe-area-inset-bottom)); }
.cookie-banner-inner {
    padding-left: calc(1.25rem + env(safe-area-inset-left));
    padding-right: calc(1.25rem + env(safe-area-inset-right));
}
/* Banners de trial/impago: son los PRIMEROS hijos de #app-view (por encima de la
   topbar) y su CTA de cobro quedaba parcialmente bajo la isla dinámica del iPhone. */
#dunning-banner,
#trial-banner {
    padding-top: calc(0.6rem + env(safe-area-inset-top));
    padding-left: calc(1rem + env(safe-area-inset-left));
}
/* Drawer lateral: en apaisado con notch se recortaba la nav, y «Salir» caía bajo
   la barra de gestos. Solo aplica cuando es drawer (fixed, <1024px). */
@media (max-width: 1023.98px) {
    .sidebar {
        padding-top: env(safe-area-inset-top);
        padding-left: env(safe-area-inset-left);
    }

    .sidebar-support {
        padding-bottom: calc(1rem + env(safe-area-inset-bottom));
    }
}
/* Contenido bajo el notch en apaisado: cada banda conserva su padding base y solo
   suma el inset del lado correspondiente. */
@media (min-width: 640px) and (max-width: 1023.98px) {
    .main-content {
        padding-left: calc(1.5rem + env(safe-area-inset-left));
        padding-right: calc(1.5rem + env(safe-area-inset-right));
    }
}
@media (max-width: 639.98px) {
    .main-content {
        padding-left: calc(0.75rem + env(safe-area-inset-left));
        padding-right: calc(0.75rem + env(safe-area-inset-right));
    }
}
#chat-launcher {
    bottom: calc(24px + env(safe-area-inset-bottom));
    right: calc(24px + env(safe-area-inset-right));
}
#onboarding-launcher {
    top: calc(14px + env(safe-area-inset-top));
    right: calc(16px + env(safe-area-inset-right));
}
@media (max-width: 639.98px) {
    #toast-container { bottom: calc(1rem + env(safe-area-inset-bottom)); }
    /* Modales full-screen: que los botones no caigan bajo la barra de gestos. */
    .modal-content,
    .modal-wide,
    #modal-pedido .modal-content {
        padding-top: calc(1.25rem + env(safe-area-inset-top));
        padding-bottom: calc(1.25rem + env(safe-area-inset-bottom));
        padding-left: calc(1rem + env(safe-area-inset-left));
        padding-right: calc(1rem + env(safe-area-inset-right));
    }

    /* Offsets del sticky de cabecera/pie y del × acompasados al padding móvil
       (1.25rem vertical + safe-area; 1rem lateral + safe-area). */
    .modal-content > h3 {
        top: calc(-1.25rem - env(safe-area-inset-top));
        margin: calc(-1.25rem - env(safe-area-inset-top)) calc(-1rem - env(safe-area-inset-right)) 1.5rem calc(-1rem - env(safe-area-inset-left));
        padding: calc(1.25rem + env(safe-area-inset-top)) calc(1rem + env(safe-area-inset-right)) 0.5rem calc(1rem + env(safe-area-inset-left));
    }

    .modal-content > .modal-actions {
        bottom: calc(-1.25rem - env(safe-area-inset-bottom));
        margin: 1.5rem calc(-1rem - env(safe-area-inset-right)) calc(-1.25rem - env(safe-area-inset-bottom)) calc(-1rem - env(safe-area-inset-left));
        padding: 0.75rem calc(1rem + env(safe-area-inset-right)) calc(1.25rem + env(safe-area-inset-bottom)) calc(1rem + env(safe-area-inset-left));
    }

    .modal-close-x {
        top: calc(-0.75rem - env(safe-area-inset-top));
        margin: calc(-0.75rem - env(safe-area-inset-top)) calc(-0.5rem - env(safe-area-inset-right)) 0 0;
    }

    /* Cabecera del chat full-screen: título y aspa fuera de la isla dinámica. */
    #chat-header {
        padding-top: calc(0.9rem + env(safe-area-inset-top));
    }
}
@media (max-width: 640px) {
    #chat-panel { width: 100%; max-width: 100%; }
    #chat-form { padding-bottom: calc(0.75rem + env(safe-area-inset-bottom)); }
}

/* ── Bloqueo de scroll de fondo con drawer o modal abiertos ───────────────── */
body.drawer-open .main-content { overflow: hidden; }
html.modal-abierto .main-content { overflow: hidden; }
.modal-content { overscroll-behavior: contain; }
/* El drawer no encadena su scroll al fondo (rebote típico de iPad Safari). */
.sidebar { overscroll-behavior: contain; }
/* Pull-to-refresh de Android: un gesto impreciso sobre el fondo recargaba la PWA
   y perdía un pedido a medias. La app es un SPA sin scroll de body (cada vista
   scrollea por dentro), así que anularlo no afecta a ningún scroll interno. */
html, body { overscroll-behavior-y: none; }

/* ── Legibilidad del dato clave y de las tablas ───────────────────────────── */
.result-highlight { font-size: 1.35rem; font-variant-numeric: tabular-nums; }
@media (max-width: 639.98px) {
    .result-highlight { font-size: 1.6rem; padding: 1rem; }
}
.table-wrap td { font-variant-numeric: tabular-nums; }

/* Edición inline: 16px para no disparar el zoom de iOS (la clase ganaba al tipo). */
@media (max-width: 1023.98px) {
    .edit-input { font-size: 16px; }
}

/* Descripciones de página: colapsables a 2 líneas en móvil/tablet. Sin tope de
   ancho: el 72ch anterior solo llegaba a aplicar en escritorio y partía en dos
   líneas las descripciones largas (Calendario, Elaboraciones, Histórico). */
.page-desc { line-height: 1.45; }
@media (max-width: 1023.98px) {
    .page-desc:not(.expanded) {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
        cursor: pointer;
    }
}

/* ── Calculadora ────────────────────────────────────────────────────────────
   NOTA: en móvil/tablet vertical (≤959.98px) NO hay NADA sticky por DECISIÓN DE
   PRODUCTO (revisión móvil 2026-06-28): los costes van al final del formulario y Guardar
   debajo, de modo que se ven antes de poder guardar (verificado por test). En escritorio
   y tablet apaisada (≥960px) el cuadro de costes SÍ es sticky (aprobado por el usuario
   2026-07-09, ver `.calc-right`); el botón Guardar nunca lo es. */

/* ── Chat de recetas: alto en dvh para que la barra de escribir no se escape ── */
@media (max-width: 1023.98px) {
    .reciachat-card { height: calc(100dvh - 260px); }
}

/* ══════════════════ CAPA TABLET (768–1023.98px) ══════════════════
   DECISIÓN del dueño (2026-07-07): en iPad vertical el menú lateral es PERSISTENTE
   desde 768px (adiós al cajón: esconder la navegación en una pantalla de 768-834px
   era fricción pura). El drawer queda solo para <768px; navigation.js lo cierra al
   cruzar el umbral. Las tablas SIGUEN como tarjetas (decisión consciente de 2026-07,
   reconfirmada): con la sidebar de 220px el contenido queda a ~550-610px. */
@media (min-width: 768px) and (max-width: 1023.98px) {
    .mobile-topbar {
        display: none;
    }

    .sidebar {
        position: static;
        transform: none;
        width: 220px;
        min-width: 220px;
        max-width: none;
        box-shadow: none;
        z-index: auto;
        transition: none;
    }

    .sidebar .sidebar-brand {
        display: flex;
    }

    /* El backdrop del drawer no pinta nada aquí (por si quedara un .show colgado). */
    .sidebar-backdrop {
        display: none;
    }

    /* Recuperar las dos dimensiones donde caben: el panel deja de ser una columna
       de 7 tiras. «Beneficio» (la 4.ª tarjeta, el titular del negocio) va a ancho
       completo, como en escritorio. */
    .dash-grid {
        grid-template-columns: repeat(2, 1fr);
    }

    .dash-chart-wide {
        grid-column: span 1;
    }

    .dash-grid > .dash-chart-card:nth-child(4) {
        grid-column: span 2;
    }
}

/* KPIs 4-en-fila solo donde de verdad caben (iPad Air/Pro vertical): a 768px el
   contenido útil (~550px) los dejaría ilegibles y se quedan en 2×2. */
@media (min-width: 900px) and (max-width: 1023.98px) {
    .dash-kpis {
        grid-template-columns: repeat(4, 1fr);
    }
}

/* ── Orientación landscape en móvil: auth y modales no se recortan ─────────── */
@media (max-height: 480px) and (orientation: landscape) {
    .modal-content,
    .modal-wide,
    #modal-pedido .modal-content { height: auto; max-height: 100dvh; }
    .auth-container { margin: 0.5rem auto; }
}

/* ── Pantallas estrechas (≤400px): cifras y escala NPS respiran ────────────── */
@media (max-width: 400px) {
    /* «1.234,56 €» a 1.55rem no cabía en dos columnas. */
    .dash-kpis {
        grid-template-columns: 1fr;
    }

    /* Escala NPS 0-10 en dos filas: 11 dianas de ~24px eran imposibles a la primera. */
    .nps-scale {
        flex-wrap: wrap;
        gap: 0.35rem;
    }

    .nps-num {
        flex: 1 1 calc(20% - 0.35rem);
        min-height: 40px;
    }
}

/* ── Móviles muy pequeños (≤360px: iPhone SE, plegables cerrados) ──────────── */
@media (max-width: 360px) {
    .main-content {
        padding-left: calc(0.6rem + env(safe-area-inset-left));
        padding-right: calc(0.6rem + env(safe-area-inset-right));
    }

    /* Doble margen del auth comprimía el contenido útil a ~280px. */
    #auth-view {
        padding: 0.5rem;
    }

    .auth-container {
        padding: 1.5rem 1rem;
        margin: 0.5rem;
    }

    /* La etiqueta «ACCIONES» repetida en cada tarjeta robaba ancho a los botones. */
    .table-wrap tbody td.acciones-cell::before {
        content: none;
    }

    /* Días de la semana sin desbordar la celda. */
    .cal-weekdays span {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* El CTA del banner (Suscribirme / Actualizar tarjeta) baja bajo el texto en
       vez de comprimirlo. */
    #trial-banner {
        flex-wrap: wrap;
    }
}


/* ══════════════════ CAPA DE ESCRITORIO GRANDE (≥1280px) ══════════════════
   La hoja era mobile-first sin NINGUNA regla por encima de 1024px: en 1440p/4K el
   contenido se estiraba sin control (tablas con huecos enormes entre columnas,
   formularios kilométricos y un dashboard de tiras planas). Esta capa acota el
   ancho de lectura, reorganiza el panel y densifica lo que gana con el espacio.
   La calculadora tiene su propia maquetación por breakpoints (ver la nota junto a
   .calc-grid): 2 columnas con costes sticky desde 960px. Desde 1024px (no 1280): la
   tablet apaisada y los portátiles pequeños también se benefician de los topes de ancho. */
@media (min-width: 1024px) {
    /* Tope de ancho por pestaña, centrado. El panel de Inicio tiene un tope mayor
       (sí aprovecha columnas); el resto lee mejor acotado. */
    .main-content > .tab-content {
        max-width: 1240px;
        margin-inline: auto;
    }

    #tab-inicio {
        max-width: 1480px;
    }

    /* KPIs con ancho máximo por tarjeta: el número dejaba de leerse perdido en una
       caja de ~570px en ultrawide. */
    .dash-kpis {
        grid-template-columns: repeat(4, minmax(0, 320px));
    }

    /* Modal de Ajustes: con nav de 180px dentro de 700px el panel quedaba estrecho;
       en escritorio hay sitio para respirar. */
    .ajustes-modal {
        width: 860px;
    }

    /* Modal de pedido: algo más de aire para el resumen de 5 métricas. */
    #modal-pedido .modal-content {
        width: min(920px, 94vw);
    }
}

/* Modales de solo lectura con tablas (ver receta / pedido / histórico): a 700px
   forzaban scroll horizontal interno en monitores que podían mostrarlos enteros. */
@media (min-width: 1280px) {
    .modal-wide {
        width: min(900px, 90vw);
    }

    .modal-wide.ajustes-modal {
        width: 860px;
    }
}

@media (min-width: 1440px) {
    /* Dashboard en rejilla real de 3 columnas: de 6 filas de tiras planas a 3 filas.
       Solo «Beneficio» (el titular del negocio, 4.ª tarjeta) conserva doble ancho. */
    .dash-grid {
        grid-template-columns: repeat(3, 1fr);
    }

    .dash-chart-wide {
        grid-column: auto;
    }

    .dash-grid > .dash-chart-card:nth-child(4) {
        grid-column: span 2;
    }

    /* Más amplitud vertical para las series (a 260px quedaban aplastadas). */
    .dash-canvas-wrap {
        height: 300px;
    }
}

@media (min-width: 1600px) {
    /* Con el contenido centrado y lejos de la esquina del FAB, parte del colchón
       inferior de 6.5rem sobra. */
    .main-content {
        padding-bottom: 4.5rem;
    }
}

/* Zebra + hover de fila en tablas de escritorio: guían la lectura fila-a-fila en
   tablas anchas (en el modo tarjeta de móvil/tablet no aplican: allí cada fila ya
   es una tarjeta con su borde). */
@media (min-width: 1024px) {
    .table-wrap tbody tr:nth-child(even) {
        background: rgba(0, 0, 0, 0.015);
    }

    .table-wrap tbody tr:hover {
        background: rgba(var(--accent-rgb), 0.05);
    }
}

/* ── Ordenación por columnas (th[data-sort], ver ui.makeSortable) ── */
.th-sortable {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.th-sortable::after {
    content: ' ↕';
    opacity: 0.35;
    font-size: 0.85em;
}

.th-sortable[aria-sort="ascending"]::after {
    content: ' ↑';
    opacity: 1;
}

.th-sortable[aria-sort="descending"]::after {
    content: ' ↓';
    opacity: 1;
}

/* Cabeceras de tabla fijas al hacer scroll en escritorio: con el contenido acotado a
   1240px las tablas ya no desbordan en horizontal, así que el overflow-x del wrapper
   (que rompería el sticky vertical) puede liberarse. */
@media (min-width: 1280px) {
    .table-wrap {
        overflow-x: visible;
    }

    .table-wrap thead th {
        position: sticky;
        top: 0;
        z-index: 2;
        background: var(--card-bg);
        box-shadow: 0 1px 0 var(--border);
    }
}

/* El checkout embebido tarda unos instantes en montar su iframe: sin altura mínima
   había un salto de layout y la caja parecía vacía/rota mientras Stripe cargaba. */
#checkout-container,
#modal-checkout-container {
    min-height: 280px;
}
