@import url('https://fonts.googleapis.com/css2?family=EB+Garamond:wght@500;600&family=Work+Sans:wght@400;500;600;700&display=swap');

:root {
    /* Fondos cálidos tomados del sitio selanna.com.gt (#FEF9F5) */
    --bg: #faf6f1;
    --bg-soft: #f2ebe2;
    --surface: #ffffff;
    --card-bg: #ffffff;

    --text: #1f1a17;
    --muted: #6b6259;

    --line: #e3d9cd;
    --border: #e3d9cd;

    /* Marca Selanna (selanna.com.gt): verde oliva como color principal de
       acción, terracota como acento y café del logo para la marca. */
    --brand: #5f6f52;
    --accent: #5f6f52;
    --brand-2: #47533d;
    --accent-strong: #47533d;
    --accent-dark: #47533d;
    --brand-soft: #e8ece3;
    --accent-soft: #e8ece3;
    --terracota: #c4661f;
    --terracota-soft: #f8e9dc;
    --cafe-logo: #783d19;

    --success: #1a7f37;
    --success-soft: #dafbe1;
    --danger: #d1242f;
    --danger-soft: #ffebe9;
    --warning: #9a6700;
    --warning-soft: #fff8c5;
    --orange: #bc4c00;
    --orange-soft: #fff1e5;

    /* Categórico para gráficas (barra de saldo tipo "Almacenamiento"): validado
       aparte de --accent/--brand, que no cumple la banda de luminosidad OKLCH
       requerida para uso categórico. Ver skill dataviz, references/palette.md. */
    --viz-blue: #2a78d6;
    --viz-orange: #eb6834;
    --dark-badge-bg: #2b2520;

    --shadow: 0 1px 0 rgba(31, 35, 40, 0.04);
    --shadow-card: 0 1px 0 rgba(31, 35, 40, 0.04);
    --shadow-hover: 0 3px 6px rgba(140, 149, 159, 0.15);
    --shadow-md: 0 8px 24px rgba(140, 149, 159, 0.2);

    --radius-xl: 10px;
    --radius-lg: 6px;
    --radius-kpi: 14px;
    --radius-pill: 999px;

    --focus-ring: rgba(95, 111, 82, 0.3);
}

:root[data-theme="dark"] {
    --bg: #12110f;
    --bg-soft: #1b1916;
    --surface: #1d1b18;
    --card-bg: #1d1b18;

    --text: #ece6de;
    --muted: #a39a8f;

    --line: #38332d;
    --border: #38332d;

    --brand: #a7b896;
    --accent: #a7b896;
    --brand-2: #c4d2b6;
    --accent-strong: #c4d2b6;
    --accent-dark: #c4d2b6;
    --brand-soft: rgba(167, 184, 150, 0.16);
    --accent-soft: rgba(167, 184, 150, 0.16);
    --terracota: #e08a4a;
    --terracota-soft: rgba(224, 138, 74, 0.16);
    --cafe-logo: #d9a47f;

    --success: #3fb950;
    --success-soft: rgba(63, 185, 80, 0.16);
    --danger: #f85149;
    --danger-soft: rgba(248, 81, 73, 0.16);
    --warning: #d29922;
    --warning-soft: rgba(210, 153, 34, 0.16);
    --orange: #ff9542;
    --orange-soft: rgba(255, 149, 66, 0.16);

    --viz-blue: #3987e5;
    --viz-orange: #d95926;
    --dark-badge-bg: #38332d;

    --shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
    --shadow-card: 0 1px 0 rgba(0, 0, 0, 0.35);
    --shadow-hover: 0 3px 10px rgba(0, 0, 0, 0.55);
    --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.5);

    --focus-ring: rgba(167, 184, 150, 0.3);
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
    font-family: "Work Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
    margin: 0;
    color: var(--text);
    background: var(--bg);
    transition: background-color .2s ease, color .2s ease;
}

a { color: var(--accent); }

h1, h2, .page-header h1, .login-card h1 {
    font-family: "EB Garamond", Georgia, "Times New Roman", serif;
    font-weight: 600;
    letter-spacing: 0.01em;
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--line); border-radius: 10px; }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

/* Franja de marca: foto del hotel (selanna.com.gt) bajo un velo verde oliva */
.brand-strip {
    position: relative;
    background: linear-gradient(120deg, rgba(52, 61, 45, 0.94), rgba(95, 111, 82, 0.88)),
                url("/static/img/selanna_fondo.jpg") center 55% / cover no-repeat;
    color: #fff;
}
header.topbar {
    position: relative;
    padding: calc(0.9rem + env(safe-area-inset-top)) 1.5rem 0.9rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.topbar-left { display: flex; align-items: center; gap: 0.9rem; }
.sidebar-toggle {
    background: transparent; border: 0; color: #f6f1ea; cursor: pointer;
    width: 34px; height: 34px; border-radius: 6px;
    display: inline-flex; align-items: center; justify-content: center; font-size: 1.1rem;
}
.sidebar-toggle:hover { background: rgba(255, 255, 255, 0.14); }
.brand { display: flex; align-items: center; gap: 0.75rem; text-decoration: none; }
.brand img { height: 26px; width: auto; display: block; }
header.topbar nav { display: flex; align-items: center; gap: 1.25rem; }
header.topbar nav a { color: #f6f1ea; text-decoration: none; font-weight: 600; font-size: 0.9rem; }
header.topbar nav a:hover { text-decoration: underline; }
.topbar-user { display: inline-flex; align-items: center; gap: 0.5rem; font-weight: 600; font-size: 0.9rem; }
.topbar-avatar {
    width: 28px; height: 28px; border-radius: 50%; object-fit: cover;
    border: 2px solid rgba(255, 255, 255, 0.35);
}

/* Toggle de tema (switch tipo iOS) */
.theme-switch {
    display: inline-flex; align-items: center; gap: 6px;
    background: transparent; border: 0; cursor: pointer; color: #f6f1ea;
}
.theme-switch-track {
    width: 38px; height: 20px; border-radius: 999px;
    background: rgba(255, 255, 255, 0.28);
    position: relative; transition: background-color .2s ease;
}
.theme-switch-knob {
    position: absolute; top: 2px; left: 2px;
    width: 16px; height: 16px; border-radius: 50%;
    background: #ffffff; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
    transition: transform .2s ease;
}
:root[data-theme="dark"] .theme-switch-knob { transform: translateX(18px); }

.dashboard-shell { width: min(1200px, calc(100% - 32px)); margin: 28px auto 40px; }
.dashboard-shell.fluid { width: calc(100% - 24px); margin: 12px auto; }

.hero {
    padding: 20px 24px;
    border: 1px solid var(--border);
    border-radius: var(--radius-xl);
    background: var(--surface);
    box-shadow: var(--shadow);
    margin-bottom: 20px;
}

.card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    overflow: hidden;
    margin-bottom: 20px;
}
.card .card-header {
    background: var(--card-bg);
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    color: var(--text);
    padding: 14px 18px;
}
.card .card-body { padding: 18px; }
/* .card tiene overflow:hidden (para los bordes redondeados) — sin este
   wrapper, una tabla más ancha que la card queda RECORTADA en vez de
   scrolleable en pantallas angostas (columnas de más simplemente invisibles,
   sin ninguna forma de llegar a ellas). */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

table { width: 100%; border-collapse: collapse; background: var(--card-bg); }
thead th {
    position: sticky; top: 0; z-index: 2;
    background: var(--bg-soft);
    color: var(--muted);
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    border-bottom: 1px solid var(--line);
    white-space: nowrap;
}
th, td { padding: 0.6rem 0.8rem; text-align: left; vertical-align: middle; }
tbody td { border-bottom: 1px solid var(--line); color: var(--text); font-size: 13px; height: 52px; }
tbody tr:nth-child(even) td { background: var(--bg-soft); }
tbody tr:hover td { background: var(--accent-soft); }

/* Columnas booleanas sí/no de los listados (get_attr en selanna_extras.py) */
.bool-icon-true { color: var(--success); }
.bool-icon-false { color: var(--danger); }

/* Columna 'Nombre' del listado de Clientes (nombre + apellido concatenados,
   ver generic_list.html/CAMPOS_CLIENTES): ancho acotado para ahorrar espacio
   horizontal — el texto salta de línea solo cuando no entra, en vez de
   estirar la tabla como el resto de las columnas (que quedan en una sola
   línea por el nowrap del header/celdas por defecto). */
.celda-ajustable { max-width: 160px; white-space: normal; word-break: break-word; }

/* Tabla con columnas ordenables al clic (ej. "Reservas registradas" del
   dashboard de Staff) — el ícono indica la dirección actual, mismo patrón de
   rotación que .sidebar-caret. */
.sortable-table th[data-sort] { cursor: pointer; user-select: none; }
.sortable-table th[data-sort]:hover { color: var(--text); }
.sortable-table .sort-icon {
    font-size: 0.7rem; margin-left: 4px; color: var(--muted);
    transition: transform 0.15s ease;
}
.sortable-table th.sort-asc .sort-icon { transform: rotate(180deg); }
.sortable-table th.sort-asc .sort-icon,
.sortable-table th.sort-desc .sort-icon { color: var(--accent); }
/* En generic_list.html el ordenamiento es un link real (recarga con
   ?ordenar=campo), no un listener JS — se resetea el estilo de <a> para que
   se vea igual que el resto de los encabezados. */
.sortable-table th[data-sort] a { color: inherit; text-decoration: none; }

/* Buscador del toolbar de cada listado de Staff (generic_list.html). El
   selector repite el patrón form input:not(...):not(...) de arriba (mismo
   nivel de especificidad + la clase .search-form) porque si no, esa regla
   genérica (width: 100%) le gana a esta y el input ocupa toda la fila,
   empujando el botón de la lupa a una línea aparte. */
.search-form { display: flex; flex-wrap: nowrap; align-items: center; gap: 4px; }
form.search-form input[type="search"]:not([type="checkbox"]):not([type="radio"]) {
    width: 200px;
    flex: 0 0 auto;
}

/* Barra de paginación (generic_list.html): página anterior/siguiente a la
   izquierda, total de registros + selector de filas por página a la derecha. */
.pagination-bar { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; margin-top: 12px; }
.por-pagina-form { display: flex; align-items: center; gap: 6px; }
.por-pagina-form select { width: auto; }

.btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 0.5rem 1rem;
    border-radius: var(--radius-lg);
    background: var(--accent);
    color: #fff;
    text-decoration: none;
    border: 1px solid transparent;
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 600;
}
.btn:hover { background: var(--accent-dark); color: #fff; }
.btn:focus, .btn:focus-visible {
    box-shadow: 0 0 0 3px var(--focus-ring);
    outline: none;
}
.btn-danger { background: var(--danger); }
.btn-secondary { background: var(--card-bg); color: var(--text); border-color: var(--border); }
.btn.disabled { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
/* Botones de exportación (ícono + texto, color suave según el formato) —
   reemplazan a un .icon-btn genérico en encabezados que ya tienen otro botón
   con texto al lado (ej. "Volver"), para que se lean como acciones y no
   como iconos sueltos. */
.btn-export-excel { background: var(--success-soft); color: var(--success); }
.btn-export-excel:hover { background: var(--success); color: #fff; }
.btn-export-pdf { background: var(--danger-soft); color: var(--danger); }
.btn-export-pdf:hover { background: var(--danger); color: #fff; }
/* Verde de marca de WhatsApp: intencional, no es un token del sistema de
   diseño porque identifica el servicio, no un estado semántico de la app. */
.btn-whatsapp { background: #25d366; color: #08341c; }
.btn-whatsapp:hover { background: #1fb855; color: #08341c; }

.icon-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px;
    border-radius: var(--radius-lg);
    background: var(--bg-soft);
    border: 1px solid var(--border);
    text-decoration: none;
    cursor: pointer;
    font-size: 15px;
    color: var(--text);
}
.icon-btn:hover { background: var(--accent-soft); border-color: var(--accent); }
.actions { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }

/* Botón de refresh junto a un <select> de catálogo (RefreshableSelect,
   selanna/forms.py + static/js/refreshable_select.js) — repuebla las opciones
   sin recargar la página. */
.refreshable-select { display: inline-flex; align-items: center; gap: 4px; max-width: 100%; }
.refreshable-select select { flex: 1; min-width: 0; }
.refreshable-select .btn-refrescar-select { flex: 0 0 auto; }
.refreshable-select .btn-refrescar-select i.girando { animation: processing-spin 0.6s linear infinite; }
/* Dentro de un formulario, a todo el ancho de la columna como el resto de
   los campos (el widget es un <span> para no romper el <p> de form.as_p). */
.form-grid .refreshable-select { display: flex; width: 100%; }

form p { margin: 0.8rem 0; }
/* Combinador de hijo directo (>): apunta solo a la etiqueta principal de cada
   campo (ej. "Grupos:"), NO a las etiquetas por-opción que Django genera dentro
   de CheckboxSelectMultiple/RadioSelect (esas quedan anidadas más profundo,
   dentro de un <div id="id_campo">, nunca como hijas directas del <p>). */
form p > label {
    display: block;
    font-weight: 600;
    margin-bottom: 6px;
    color: var(--text);
}
form input:not([type="checkbox"]):not([type="radio"]), form select, form textarea {
    width: 100%;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--card-bg);
    color: var(--text);
    transition: border-color 0.1s ease, box-shadow 0.1s ease;
}
form input:not([type="checkbox"]):not([type="radio"]):focus, form select:focus, form textarea:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--focus-ring);
    outline: none;
}
form input[readonly] {
    background: var(--bg-soft);
    color: var(--muted);
    cursor: not-allowed;
}
form input[type="checkbox"], form input[type="radio"] { width: auto; }

/* Layout de 2 columnas para formularios en escritorio (envolver los <p> de
   `form.as_p`, o los renderizados campo a campo, en <div class="form-grid">).
   Campos donde no tiene sentido compartir fila con otro (textarea, grupos de
   checkbox/radio, archivos, o cualquier <p>/bloque marcado a mano con
   .form-grid-full) ocupan las 2 columnas completas. Por debajo de 700px
   (mobile) vuelve a 1 columna, igual que el resto de los grids del sistema
   (ver .kpi-grid). */
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 24px; }
.form-grid > p:has(textarea),
.form-grid > p:has(input[type="checkbox"]),
.form-grid > p:has(input[type="file"]),
.form-grid > p:has(select[multiple]),
.form-grid .form-grid-full {
    grid-column: 1 / -1;
}
/* "Documento de respaldo" (venta_form.html) es la excepción: un solo botón
   "Seleccionar archivo" corto, entra cómodo a la par de "Estado venta" en
   vez de ocupar su propia fila completa. El selector por ID (más específico
   que `input[type="file"]` de arriba) solo pisa la regla genérica para este
   campo puntual, sin afectar el resto de los inputs de archivo del sistema
   (DocumentoCliente/DocumentoInstructor, que sí necesitan la fila completa). */
.form-grid > p:has(#id_documento_respaldo) { grid-column: auto; }
@media (max-width: 700px) { .form-grid { grid-template-columns: 1fr; } }

/* Selector de fecha con formato largo (ej. "Domingo 30 de agosto 2026"): el
   <input> real (data-input) queda invisible pero SIGUE ocupando el mismo
   rectángulo que el <label> (position: absolute; inset: 0) — a propósito, no
   display:none: Flatpickr en modo "wrap" (ver flatpickr_init.js) calcula
   dónde dibujar el calendario según la posición/tamaño de ese input, y con
   display:none le queda un rectángulo de 0x0 en (0,0) y el calendario
   termina flotando en la esquina superior izquierda de la página en vez de
   debajo del <span data-toggle> (bug real encontrado al verificar con
   Playwright antes de este ajuste). pointer-events: none porque el click ya
   lo captura el <span> (visualmente encima, mismo <label>). */
.fecha-picker { position: relative; display: inline-flex; align-items: center; cursor: pointer; }
.fecha-larga {
    display: inline-flex; align-items: center; gap: 8px; padding: 0.5rem 1rem; white-space: nowrap;
    border: 1px solid var(--border); border-radius: var(--radius-lg);
    background: var(--card-bg); color: var(--text); font-weight: 500; font-size: 0.85rem;
}
.fecha-larga .bi-calendar3 { color: var(--muted); font-size: 0.9rem; }
.fecha-picker input { position: absolute; inset: 0; opacity: 0; pointer-events: none; padding: 0; border: 0; }

/* Flatpickr (selectores de fecha/fecha-hora, ver flatpickr_init.js): apenas lo
   necesario para que el popup del calendario respete el tema claro/oscuro de
   la app en vez de quedarse con el blanco fijo de su tema por default —
   reusa los mismos tokens que el resto de selanna.css, sin theme aparte de
   Flatpickr. */
.flatpickr-calendar {
    background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md); font-family: inherit;
}
.flatpickr-calendar.arrowTop::before,
.flatpickr-calendar.arrowTop::after { border-bottom-color: var(--border); }
.flatpickr-months .flatpickr-month,
.flatpickr-current-month,
.flatpickr-weekdays { background: var(--card-bg); color: var(--text); fill: var(--text); }
.flatpickr-weekday { color: var(--muted); }
span.flatpickr-weekday { background: var(--card-bg); }
.flatpickr-day { color: var(--text); }
.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover { color: var(--muted); opacity: 0.4; }
.flatpickr-day.prevMonthDay, .flatpickr-day.nextMonthDay { color: var(--muted); }
.flatpickr-day:hover, .flatpickr-day.today:hover { background: var(--bg-soft); border-color: var(--bg-soft); }
.flatpickr-day.today { border-color: var(--accent); }
.flatpickr-day.selected, .flatpickr-day.selected:hover {
    background: var(--accent); border-color: var(--accent); color: #fff;
}
.flatpickr-current-month input.cur-year { color: var(--text); }
.flatpickr-current-month .flatpickr-monthDropdown-months { background: var(--card-bg); color: var(--text); }
.numInputWrapper span.arrowUp:after { border-bottom-color: var(--text); }
.numInputWrapper span.arrowDown:after { border-top-color: var(--text); }
.flatpickr-time input, .flatpickr-time .flatpickr-am-pm {
    color: var(--text); background: var(--card-bg);
}
.flatpickr-time .numInputWrapper:hover, .flatpickr-time input:hover,
.flatpickr-am-pm:hover, .flatpickr-am-pm:focus { background: var(--bg-soft); }

/* Grupos del formulario de Usuario: una opción por tarjeta, texto arriba y
   checkbox abajo (el campo se comporta como selección única, ver JS en
   usuario_form.html + validación server-side en forms.py). */
#id_groups { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 4px; }
#id_groups > div {
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 10px 14px;
    background: var(--card-bg);
}
#id_groups label {
    display: flex; flex-direction: column-reverse; align-items: flex-start; gap: 8px;
    font-weight: 600; cursor: pointer;
}

/* Sección "Datos de Cliente/Instructor" del formulario de Usuario: se muestra
   solo cuando el grupo elegido es Cliente o Instructor (ver JS en
   usuario_form.html); display:none por defecto para evitar un parpadeo visible
   antes de que corra el JS. */
.datos-persona-card {
    display: none;
    margin-top: 8px; padding: 14px 16px;
    border: 1px solid var(--border); border-radius: var(--radius-lg);
    background: var(--bg-soft);
}
.datos-persona-card h3 { margin: 0 0 4px; font-size: 1rem; }
.datos-persona-card .sub { color: var(--muted); font-size: 0.85rem; margin: 0 0 10px; }

form ul.errorlist { color: var(--danger); font-size: 0.85rem; margin: 0.2rem 0; padding-left: 1rem; }
.non-field-errors { background: var(--danger-soft); color: var(--danger); padding: 10px 14px;
    border-radius: var(--radius-lg); margin-bottom: 14px; font-size: 0.9rem; }

.page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; gap: 1rem; flex-wrap: wrap; }
.page-header h1 { font-size: 1.3rem; margin: 0; }

/* Título + navegador de fecha centrado + acciones, los tres en una sola línea
   (calendarios de Staff): a diferencia de .page-header (flex de 2 puntas), acá
   son 3 bloques y el del medio queda centrado de verdad sin importar cuánto
   midan los de los costados. */
.page-header-cal { display: grid; grid-template-columns: 1fr auto 1fr; }
.page-header-cal h1 { justify-self: start; }
.page-header-cal .cal-fecha-nav { justify-self: center; }
.page-header-cal > .actions:last-child { justify-self: end; }

/* En móvil la fecha larga no entra junto a los botones anterior/siguiente en
   una sola línea, y el flex-wrap de .actions los reparte en 3 líneas propias
   (anterior / fecha / siguiente). Forzamos la fecha a ocupar toda la línea
   (order -1 + width 100%) para que ambos botones, al no entrar junto a ella,
   caigan juntos en la línea siguiente, debajo de la fecha. */
@media (max-width: 768px) {
    .cal-fecha-nav { justify-content: center; }
    .cal-fecha-nav .fecha-picker { order: -1; width: 100%; justify-content: center; }
}

.badge {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 10px;
    border-radius: var(--radius-pill);
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}
.badge-success { background: var(--success-soft); color: var(--success); }
.badge-danger  { background: var(--danger-soft);  color: var(--danger); }
.badge-warning { background: var(--warning-soft); color: var(--warning); }
.badge-orange  { background: var(--orange-soft);  color: var(--orange); }
.badge-accent  { background: var(--accent-soft);  color: var(--accent-dark); }
.badge-neutral { background: rgba(101, 109, 118, 0.14); color: var(--muted); }
.badge-dark { background: var(--dark-badge-bg); color: #fff; }

/* Badge "sólido" (fondo de color lleno + texto blanco), para el estado
   activo/inactivo de clientes/instructores/usuarios en los listados. */
.badge-solid {
    display: inline-flex; align-items: center;
    padding: 2px 10px;
    border-radius: var(--radius-pill);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.03em;
    color: #fff;
    white-space: nowrap;
}
.badge-solid-success { background: var(--success); }
.badge-solid-danger { background: var(--danger); }

/* --- Miniatura de foto en listados + modal de zoom --- */
.foto-thumb {
    width: 36px; height: 36px; border-radius: 50%; object-fit: cover;
    border: 1px solid var(--border); cursor: pointer; display: block;
    transition: transform 0.15s ease;
}
.foto-thumb:hover { transform: scale(1.08); }
.foto-thumb-empty {
    display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; border-radius: 50%;
    background: var(--bg-soft); border: 1px solid var(--border); color: var(--muted);
    cursor: default; font-size: 16px;
}
/* Archivo de documento que no es imagen (PDF, etc.): mismo tamaño/forma que
   foto-thumb-empty pero SÍ es clicable — abre el archivo en pestaña nueva
   (reemplaza al botón "Ver archivo" que antes iba aparte). */
.foto-thumb-file {
    display: inline-flex; align-items: center; justify-content: center;
    width: 36px; height: 36px; border-radius: 50%;
    background: var(--bg-soft); border: 1px solid var(--border); color: var(--text);
    cursor: pointer; font-size: 16px; text-decoration: none;
    transition: transform 0.15s ease;
}
.foto-thumb-file:hover { transform: scale(1.08); background: var(--accent-soft); color: var(--accent-dark); }
/* Miniaturas de la tabla "Respaldos" (Venta/Reserva) un poco más grandes que
   el resto de usos de .foto-thumb (avatares, Documentos de Cliente/
   Instructor) — a pedido del usuario, acotado con esta clase para no
   afectar esos otros usos. */
.tabla-respaldos .foto-thumb,
.tabla-respaldos .foto-thumb-empty,
.tabla-respaldos .foto-thumb-file {
    width: 52px; height: 52px; font-size: 20px;
}
.foto-modal {
    display: none; position: fixed; inset: 0; z-index: 8000;
    background: rgba(0, 0, 0, 0.78);
    align-items: center; justify-content: center;
}
.foto-modal.open { display: flex; }
.foto-modal img {
    max-width: min(90vw, 720px); max-height: 85vh;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
}
.foto-modal-close {
    position: absolute; top: 20px; right: 28px;
    color: #fff; font-size: 1.6rem; cursor: pointer; opacity: 0.85;
}
.foto-modal-close:hover { opacity: 1; }

.info-modal {
    display: none; position: fixed; inset: 0; z-index: 8000;
    background: rgba(0, 0, 0, 0.55);
    align-items: center; justify-content: center;
}
.info-modal.open { display: flex; }
.info-modal-panel {
    width: min(90vw, 560px); max-height: 80vh; overflow-y: auto;
    background: var(--card-bg); border: 1px solid var(--border);
    border-radius: var(--radius-lg); box-shadow: var(--shadow-md);
}
.info-modal-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 14px 18px; border-bottom: 1px solid var(--border);
}
.info-modal-header h2 { margin: 0; font-size: 1.05rem; }
.info-modal-close { color: var(--muted); cursor: pointer; }
.info-modal-close:hover { color: var(--text); }
.info-modal-body { padding: 14px 18px; }
.info-modal-body h3 { font-size: 0.85rem; color: var(--muted); text-transform: uppercase; margin: 14px 0 6px; }
.info-modal-body h3:first-child { margin-top: 0; }
.info-modal-body ul { margin: 0 0 4px; padding-left: 1.2rem; }
.info-modal-body li { font-size: 0.9rem; margin-bottom: 4px; }

.messages { list-style: none; margin: 0 0 16px; padding: 0; }
.messages li {
    padding: 10px 14px; border-radius: var(--radius-lg); margin-bottom: 8px; font-size: 0.9rem;
    background: var(--accent-soft); color: var(--accent-dark);
}
.messages li.error { background: var(--danger-soft); color: var(--danger); }
.messages li.success { background: var(--success-soft); color: var(--success); }
.messages li.warning { background: var(--warning-soft); color: var(--warning); }
.text-muted { color: var(--muted); }

/* Website Contenido (artículos de WordPress): editor Quill retemado con los
   tokens del tema (claro/oscuro) y vista previa del contenido publicado. */
.articulo-web-editor { min-height: 320px; background: var(--card-bg); color: var(--text); font-size: 0.95rem; }
.ql-toolbar.ql-snow, .ql-container.ql-snow { border-color: var(--border); }
.ql-toolbar.ql-snow { background: var(--bg-soft); border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
.ql-container.ql-snow { border-radius: 0 0 var(--radius-lg) var(--radius-lg); }
.ql-snow .ql-stroke { stroke: var(--text); }
.ql-snow .ql-fill { fill: var(--text); }
.ql-snow .ql-picker { color: var(--text); }
.ql-snow .ql-picker-options { background: var(--card-bg); }
.ql-editor.ql-blank::before { color: var(--muted); }
.articulo-web-imagen { max-width: 100%; max-height: 260px; border-radius: var(--radius-lg); display: block; }
.articulo-web-contenido { line-height: 1.6; }
.articulo-web-contenido img { max-width: 100%; height: auto; }
/* Historial de versiones (lista de cambios y comparación bloque a bloque). */
.version-celda { min-width: 220px; max-width: 420px; white-space: normal; word-break: break-word; }
.version-cambios { margin: 0; padding-left: 18px; }
.version-cambios li { margin: 2px 0; }
.version-diff { display: flex; flex-direction: column; gap: 4px; font-size: 0.92rem; line-height: 1.5; }
.version-diff > div { padding: 6px 10px; border-radius: var(--radius-lg); border-left: 3px solid transparent; white-space: pre-wrap; word-break: break-word; }
.version-diff-igual { color: var(--muted); }
.version-diff-omitido { color: var(--muted); font-style: italic; text-align: center; }
.version-diff-agregado { background: var(--success-soft); border-left-color: var(--success) !important; }
.version-diff-quitado { background: var(--danger-soft); border-left-color: var(--danger) !important; text-decoration: line-through; }
.version-celda-ancha { min-width: 420px; white-space: normal; }
td.version-diff-quitado-suave { background: var(--danger-soft); }
td.version-diff-agregado-suave { background: var(--success-soft); }
/* Páginas Elementor del website: campos editables agrupados por bloque. */
.pagina-web-campos { display: flex; flex-direction: column; gap: 12px; }
.pagina-web-campo { word-break: break-word; }
.pagina-web-campo .articulo-web-contenido { margin-top: 4px; }
.pagina-web-campo input[type="text"] { width: 100%; max-width: 640px; }
.pagina-web-editor .articulo-web-editor { min-height: 140px; }
/* Selector de editor "Quill | TinyMCE" (control segmentado) y página de preferencia. */
.editor-html-cabecera { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 16px 0 8px; }
.editor-selector { display: inline-flex; align-items: center; gap: 6px; font-size: 0.85rem; color: var(--muted); }
.editor-selector button {
    border: 1px solid var(--border); background: var(--card-bg); color: var(--text);
    padding: 4px 12px; cursor: pointer; font: inherit; margin-left: -1px;
}
.editor-selector button:first-of-type { border-radius: var(--radius-lg) 0 0 var(--radius-lg); }
.editor-selector button:last-of-type { border-radius: 0 var(--radius-lg) var(--radius-lg) 0; }
.editor-selector button.activo { background: var(--accent-soft); color: var(--accent-dark); border-color: var(--accent-dark); position: relative; }
.editor-opciones { display: flex; flex-direction: column; gap: 10px; max-width: 720px; }
.editor-opcion { display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius-lg); cursor: pointer; }
.editor-opcion.activo, .editor-opcion:has(input:checked) { border-color: var(--accent-dark); background: var(--accent-soft); }
.editor-opcion .helptext { display: block; margin-top: 2px; }
.tox-tinymce { border-color: var(--border) !important; border-radius: var(--radius-lg) !important; }

.kpi-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; margin-bottom: 20px; }
@media (max-width: 900px) { .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.kpi {
    padding: 16px 18px; border-radius: var(--radius-kpi); background: var(--card-bg);
    border: 1px solid var(--border); box-shadow: var(--shadow-card);
}
.kpi .kpi-value { font-size: 24px; font-weight: 600; }
.kpi .kpi-value-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.kpi .kpi-label { font-size: 12px; font-weight: 600; color: var(--muted); }
/* Utilidad genérica de texto en rojo — saldo negativo (columna "Saldo hrs"
   del listado de Clientes, cajas "Saldo en Horas" de Staff/portal). */
.text-danger { color: var(--danger); }
/* .card .card-header (2 clases) le gana en especificidad a .text-danger (1
   clase) sola — repetir con la clase encima para la alerta de "Cuenta por
   cobrar" en cliente_detail.html/portal/home.html. */
.card .card-header.text-danger { color: var(--danger); }

/* Caja de saldo con barra segmentada (portal cliente), estilo "Almacenamiento"
   de iOS: franja de color por categoría reservada + resto libre sin colorear.
   Ocupa 2 columnas del kpi-grid para que la barra entre cómoda. */
.kpi-storage { grid-column: span 2; }
/* En pantallas amplias, las 3 cajas de saldo entran en una sola fila (aunque
   queden un poco apretadas) en vez de 2 por fila. */
.kpi-grid-storage { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.kpi-grid-storage .kpi-storage { grid-column: span 1; }
@media (max-width: 900px) { .kpi-grid-storage { grid-template-columns: 1fr; } }
.kpi-storage-header { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.kpi-storage-title { font-size: 13px; font-weight: 600; color: var(--text); }
.kpi-storage-summary { font-size: 12px; color: var(--muted); white-space: nowrap; }
.kpi-storage-bar-row { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.kpi-storage-bar {
    flex: 1; display: flex; height: 20px;
    border-radius: var(--radius-pill); background: var(--bg-soft);
    border: 1px solid var(--border); overflow: hidden;
}
.kpi-storage-seg { height: 100%; }
.kpi-storage-seg + .kpi-storage-seg { margin-left: 2px; }
/* Mismos colores que la leyenda de estados (kpi-storage-dot-estado-*) para
   que el segmento de la barra y su punto en la leyenda se identifiquen a
   simple vista — "cancelado" no tiene segmento propio a propósito (libera
   saldo, no ocupa franja de la barra, ver services.horas_consumidas). */
.kpi-storage-seg-reservado { background: #f59e0b; } /* amber-500 */
.kpi-storage-seg-completado { background: #22c55e; } /* green-500 */
.kpi-storage-seg-noshow { background: #111827; } /* gray-900 */
/* Barrita de % en las cajas "Horas Usadas"/"Saldo en Horas" del detalle de
   Cliente (Staff) — a diferencia de los colores de arriba (fijos, calcan
   Tailwind a propósito), este usa el accent del tema para adaptarse a
   claro/oscuro, ya que no es un estado de Reserva sino solo saldo. */
.kpi-storage-seg-saldo { background: var(--accent); }
.kpi-storage-free { font-size: 12px; font-weight: 600; color: var(--muted); white-space: nowrap; }
.kpi-storage-legend {
    display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 8px;
    font-size: 11px; color: var(--muted);
}
.kpi-storage-dot {
    display: inline-block; width: 8px; height: 8px; border-radius: 50%;
    margin-right: 5px; vertical-align: middle;
}
/* Colores literales de Tailwind (no adaptados a claro/oscuro a propósito —
   son los mismos hex que usa Tailwind, pedido así) para la leyenda de
   estados de Reserva en las cajas de saldo del portal de Clientes. */
.kpi-storage-dot-estado-reservado { background: #f59e0b; } /* amber-500 */
.kpi-storage-dot-estado-completado { background: #22c55e; } /* green-500 */
.kpi-storage-dot-estado-noshow { background: #111827; } /* gray-900 */

/* --- Calendario matriz --- */
/* Contenedor con altura acotada para que el thead (sticky top:0 por regla
   global) tenga de verdad un scroll interno del cual "congelarse" — sin
   max-height, la página entera scrollea y el sticky no tiene efecto visible
   (mismo patrón que .daycal-scroll para el calendario de Staff). */
.cal-table-scroll { overflow: auto; max-height: 70vh; }
.cal-table td, .cal-table th { border: 1px solid var(--line); font-size: 12px; }
.cal-table th .foto-thumb { width: 28px; height: 28px; display: block; margin: 0 auto 4px; }
.cal-cell-libre { background: var(--card-bg); }
.cal-cell-reserva { background: var(--accent-soft); }
.cal-cell-bloqueo { background: repeating-linear-gradient(45deg, var(--bg-soft), var(--bg-soft) 6px, var(--border) 6px, var(--border) 12px); color: var(--muted); }
.cal-cell-pasado { background: var(--bg-soft); color: var(--muted); text-align: center; opacity: 0.6; }
.cal-hora { background: var(--bg-soft); font-weight: 700; white-space: nowrap; }
.cal-cell-link { display: block; text-align: center; color: var(--muted); text-decoration: none; padding: 4px; }
.cal-cell-link:hover { background: var(--accent-soft); color: var(--accent); border-radius: 4px; }

/* --- Calendario de Staff (día), por servicio o por instructor: una sola
   grilla CSS compartida por todas las columnas (header + cuerpo van juntos
   dentro de .daycal-scroll para desplazarse horizontalmente sincronizados).
   Cada fila representa 15 min (minmax(12px, auto)): si el contenido de una
   cápsula no entra en su tramo de filas, esa fila crece sola y arrastra a
   todas las columnas por igual — nunca se desalinean las líneas de hora. */
/* overflow acotado: si la grilla crece más de 70vh (filas auto por cápsulas
   con mucho contenido) o hay muchas columnas, aparece scroll — la columna de
   hora y el encabezado quedan "congelados" (sticky) en vez de perderse. */
/* -webkit-overflow-scrolling: touch — sin esto, el swipe horizontal para ver
   todas las columnas no respondía en Safari/iOS (el calendario de Staff vive
   dentro de un <iframe>, y el scroll táctil anidado ahí es poco confiable
   sin esta propiedad; el portal de Clientes no la necesita porque no usa
   iframe). El .card-body que envuelve esto en calendario_servicio.html/
   calendario_instructor.html ya NO lleva su propio overflow-x:auto — dos
   contenedores de scroll anidados era parte del mismo problema. */
.daycal-scroll { overflow: auto; max-height: 70vh; -webkit-overflow-scrolling: touch; }
.daycal-header-row, .daycal-grid { display: grid; column-gap: 1px; background: var(--border); }
.daycal-header-row { border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 3; }
.daycal-header-gutter, .daycal-col-bg { background: var(--card-bg); }
.daycal-header-gutter { position: sticky; left: 0; z-index: 4; }
.daycal-col-bg-gutter, .daycal-hora-label { position: sticky; left: 0; z-index: 2; }
.daycal-header-col {
    padding: 6px 8px; font-size: 12px; font-weight: 700; text-align: center;
    background: var(--bg-soft); white-space: nowrap;
    display: flex; align-items: center; justify-content: center; gap: 6px;
}
.daycal-header-col .foto-thumb { width: 28px; height: 28px; }
.daycal-grid {
    position: relative;
    /* 56 filas de 15 min (05:00-19:00) — debe coincidir con TOTAL_FILAS en selanna/views/agenda.py */
    grid-template-rows: repeat(56, minmax(12px, auto));
}
.daycal-hora-label {
    /* translateY menor a -50% (no queda centrada en la línea de hora) a
       propósito: con -50% la etiqueta de las 05:00 (primera fila) quedaba
       tapada por el borde superior de la grilla, que le recorta la mitad. */
    grid-column: 1; align-self: start; transform: translateY(-15%);
    font-size: 11px; color: var(--muted); font-weight: 700; text-align: right;
    padding-right: 6px; background: var(--card-bg);
}
.daycal-linea { grid-column: 1 / -1; align-self: start; height: 0; border-top: 1px solid var(--line); }
.daycal-col-bg { border-right: 1px solid var(--border); }
.daycal-evento {
    align-self: stretch; margin: 1px 3px;
    border-radius: var(--radius-lg);
    padding: 3px 6px; font-size: 11px; line-height: 1.35;
    box-shadow: var(--shadow-card);
}
.daycal-evento-reserva {
    position: relative; z-index: 1; display: block; cursor: pointer;
    background: var(--accent-soft); color: var(--text); border-left: 3px solid var(--accent);
    text-decoration: none;
}
.daycal-evento-reserva:hover { color: var(--text); box-shadow: var(--shadow-hover); }
.daycal-evento-bloqueo {
    background: repeating-linear-gradient(45deg, var(--bg-soft), var(--bg-soft) 6px, var(--border) 6px, var(--border) 12px);
    color: var(--muted);
}

/* Celda libre clicable para agregar reserva (calendario de Staff): invisible
   hasta el hover, para no llenar de "+" un calendario con muchos huecos. */
.daycal-slot-add {
    display: flex; align-items: center; justify-content: center;
    margin: 1px 3px; border-radius: var(--radius-lg);
    color: transparent; text-decoration: none; font-size: 13px;
}
.daycal-slot-add:hover { background: var(--accent-soft); color: var(--accent); }

/* Pestañas Día/Semana/Mes de los calendarios de Staff (calendario_servicio/
   calendario_instructor) — reusa .btn/.btn-secondary, solo agrupa. */
.cal-vista-tabs { display: inline-flex; gap: 4px; }

/* Vistas Semana/Mes: grilla visual estilo Google Calendar (encabezado de
   días + celdas en cuadrícula con borde) — clic en cualquier celda navega a
   la vista Día de esa fecha (ver _calendario_semana.html/_calendario_mes.html/
   _calendario_dias_header.html, selanna/views/agenda.py). Mes, por default
   (switch "Resumen" en ON), solo muestra la CANTIDAD de reservas (celda
   chica, `.cal-dia-badge`); con el switch en OFF (`.cal-mes-grid-detalle`)
   Mes lista el detalle de cada reserva igual que Semana (`.cal-dia-reservas`,
   compartido — ver _reservas_dia_detalle.html). */
.cal-dias-header, .cal-semana-grid, .cal-mes-grid {
    display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px;
}
.cal-dias-header {
    font-size: 12px; font-weight: 700; color: var(--muted); text-align: center;
    margin-bottom: 4px;
}
/* Encabezado Lun-Dom "congelado" (freeze) de la vista Mes: mismo patrón que
   .daycal-header-row/.daycal-scroll de la vista Día — scroll acotado
   (max-height) en vez de dejar crecer/scrollear la página del iframe
   entero, y el encabezado sticky dentro de ESE contenedor. En modo resumen
   (grilla corta) nunca llega a activarse scroll, así que no cambia nada
   visualmente; sirve sobre todo con el switch "Resumen" en OFF, donde el
   modo detalle puede crecer más que la pantalla. */
.cal-mes-scroll { overflow: auto; max-height: 70vh; -webkit-overflow-scrolling: touch; }
.cal-mes-scroll .cal-dias-header {
    position: sticky; top: 0; z-index: 3; background: var(--card-bg);
    padding-top: 2px; padding-bottom: 4px; margin-bottom: 0;
}
.cal-mes-grid { grid-auto-rows: 1fr; }
/* Modo detalle de Mes: cada fila (semana) crece según su propio día más
   cargado, igual que Semana — `1fr` forzaría todas las semanas a la misma
   altura (la del mes entero), no tiene sentido con contenido variable. */
.cal-mes-grid-detalle { grid-auto-rows: auto; }
.cal-mes-grid-detalle .cal-dia-celda { min-height: 140px; padding: 10px 8px; align-items: stretch; }

/* Switch "Resumen" de la vista Mes (calendario_servicio.html/
   calendario_instructor.html) — patrón checkbox-hack: el track/thumb son
   ::before/::after de un <span> hermano del <input>, sin JS de animación,
   solo el onchange que ya reenvía el form (mismo criterio que el resto de
   controles del header del calendario). */
.switch-toggle {
    display: inline-flex; align-items: center; gap: 8px;
    font-size: 13px; font-weight: 600; color: var(--text); cursor: pointer; user-select: none;
}
.switch-toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
    position: relative; width: 34px; height: 20px; border-radius: var(--radius-pill);
    background: var(--border); transition: background 0.15s ease; flex-shrink: 0;
}
.switch-track::after {
    content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
    border-radius: 50%; background: #fff; box-shadow: var(--shadow-hover);
    transition: transform 0.15s ease;
}
.switch-toggle input:checked + .switch-track { background: var(--accent); }
.switch-toggle input:checked + .switch-track::after { transform: translateX(14px); }
.switch-toggle input:focus-visible + .switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Mi cuenta > Notificaciones por correo: interruptor general destacado +
   una caja por tipo de notificación (grilla que se acomoda al ancho, 1
   columna en móvil). Dentro de cada caja, una fila por opción con el texto
   a la izquierda y el switch alineado a la derecha. */
.notif-ayuda { font-size: 12px; color: var(--muted); font-weight: 400; }
.notif-general {
    display: flex; align-items: center; gap: 14px;
    padding: 14px 16px; border: 1px solid var(--border); border-radius: var(--radius-xl);
    background: var(--accent-soft);
}
.notif-general-icono,
.notif-grupo-icono {
    display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
    border-radius: 50%; background: var(--accent); color: #fff;
}
.notif-general-icono { width: 36px; height: 36px; font-size: 17px; }
.notif-general-texto { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.notif-general-texto strong { font-size: 14px; color: var(--text); }
.notif-grupos {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 16px; margin-top: 16px; transition: opacity 0.15s ease;
}
.notif-grupos.notif-inactivas { opacity: 0.45; }
.notif-grupo {
    border: 1px solid var(--border); border-radius: var(--radius-xl);
    background: var(--card-bg); box-shadow: var(--shadow-card); overflow: hidden;
}
.notif-grupo-header {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 14px; background: var(--bg-soft); border-bottom: 1px solid var(--border);
}
.notif-grupo-icono { width: 26px; height: 26px; font-size: 13px; }
.notif-grupo-header h3 { margin: 0; font-size: 13px; font-weight: 700; color: var(--text); }
.notif-grupo-body { padding: 4px 14px; }
.notif-opcion {
    position: relative; display: flex; justify-content: space-between; gap: 12px;
    padding: 10px 0; font-weight: 400;
}
.notif-opcion + .notif-opcion { border-top: 1px solid var(--line); }
.notif-general .switch-toggle { position: relative; }
.notif-nota {
    display: flex; gap: 8px; align-items: flex-start; margin: 16px 0 0;
    padding: 10px 14px; border-radius: var(--radius-lg);
    background: var(--bg-soft); font-size: 12px; color: var(--muted);
}

.cal-dia-celda {
    display: flex; flex-direction: column; align-items: center;
    gap: 8px; padding: 10px 6px;
    border: 1px solid var(--border); border-radius: var(--radius-lg);
    background: var(--card-bg);
    color: var(--text); text-decoration: none;
    transition: background 0.15s ease, box-shadow 0.15s ease;
}
.cal-semana-grid .cal-dia-celda { min-height: 140px; padding: 10px 8px; align-items: stretch; }
.cal-mes-grid .cal-dia-celda { min-height: 84px; }
.cal-dia-celda:hover { background: var(--bg-soft); box-shadow: var(--shadow-hover); }

.cal-dia-fuera { color: var(--muted); opacity: 0.5; }

.cal-dia-numero {
    align-self: center;
    display: flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; border-radius: 50%;
    font-size: 15px; font-weight: 700;
}
.cal-dia-hoy .cal-dia-numero { background: var(--accent); color: #fff; }

.cal-dia-badge {
    margin-top: auto;
    display: inline-flex; align-items: center; justify-content: center;
    padding: 3px 10px; border-radius: var(--radius-pill);
    background: var(--accent-soft); color: var(--accent-dark);
    font-size: 11px; font-weight: 700; white-space: nowrap;
}

/* Detalle de reservas en la vista Semana (una por día, sin scroll propio —
   la celda crece lo que haga falta según la cantidad de reservas de ese
   día; al ser todas las celdas de una misma fila de grid, las demás celdas
   de la semana se estiran parejo a la altura del día más cargado) — línea
   suave (.cal-dia-reserva-item + .cal-dia-reserva-item, no antes de la
   primera ni después de la última) separando cada reserva. */
.cal-dia-reservas {
    display: flex; flex-direction: column;
}
.cal-dia-reserva-item { padding: 4px 0; }
.cal-dia-reserva-item + .cal-dia-reserva-item { border-top: 1px solid var(--line); }
.cal-reserva-linea1 { display: flex; gap: 6px; align-items: baseline; }
.cal-reserva-hora { font-size: 11px; font-weight: 700; color: var(--accent-dark); flex-shrink: 0; }
.cal-reserva-cliente, .cal-reserva-linea2 {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-reserva-cliente { font-size: 11px; }
.cal-reserva-linea2 { font-size: 10px; color: var(--muted); }

@media (max-width: 768px) {
    .cal-dias-header { font-size: 10px; }
    .cal-semana-grid .cal-dia-celda { min-height: 110px; }
    .cal-mes-grid .cal-dia-celda { min-height: 56px; padding: 6px 2px; gap: 4px; }
    .cal-mes-grid-detalle .cal-dia-celda { min-height: 110px; padding: 10px 8px; }
    .cal-dia-numero { width: 20px; height: 20px; font-size: 12px; }
    .cal-dia-badge { padding: 2px 6px; font-size: 10px; }
    .cal-reserva-linea2 { display: none; }
}

/* --- Login estilo Salesforce --- */
.login-shell {
    min-height: 100vh; display: grid; grid-template-columns: 1.1fr 1fr;
    background: var(--bg);
}
@media (max-width: 860px) { .login-shell { grid-template-columns: 1fr; } .login-shell .login-side { display: none; } }
.login-side {
    background: linear-gradient(150deg, rgba(52, 61, 45, 0.55), rgba(120, 61, 25, 0.35)),
                url("/static/img/selanna_fondo.jpg") center 60% / cover no-repeat;
    display: flex; flex-direction: column; justify-content: center; align-items: center; color: #fff;
    padding: 40px;
}
.login-side p { max-width: 320px; text-align: center; opacity: 0.9; }
.login-main { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 24px; }
.login-main-logo { height: 44px; width: auto; max-width: 100%; display: block; margin-bottom: 24px; }
.login-side-logo { height: 34px; width: auto; max-width: 100%; display: block; margin-bottom: 16px; }
/* Logo a color en tema claro, logo blanco en tema oscuro. */
.logo-tema-oscuro { display: none; }
:root[data-theme="dark"] .logo-tema-claro { display: none; }
:root[data-theme="dark"] .logo-tema-oscuro { display: block; }
.login-card {
    width: 100%; max-width: 380px; background: var(--card-bg); border: 1px solid var(--border);
    border-radius: var(--radius-xl); box-shadow: var(--shadow-md); padding: 32px;
}
.login-card h1 { font-size: 1.3rem; margin: 0 0 6px; }
.login-card .sub { color: var(--muted); font-size: 0.9rem; margin-bottom: 24px; }
.login-card .btn { width: 100%; justify-content: center; padding: 0.7rem; margin-top: 8px; }
.login-signup {
    margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--border);
    text-align: center;
}
.login-signup p { color: var(--muted); font-size: 0.85rem; margin: 0 0 10px; }
.login-website { margin-top: 4px; text-align: center; }
/* Campo de contraseña con botón de mostrar/ocultar (ojito) — reusado en el
   login y en el alta de Usuario (password1/password2), no solo en .login-card. */
.password-field { position: relative; display: block; }
.password-field input { padding-right: 2.4rem; }
.password-toggle {
    position: absolute; top: 50%; right: 0.6rem; transform: translateY(-50%);
    background: none; border: none; padding: 4px; cursor: pointer;
    color: var(--muted); display: flex; align-items: center;
}
.password-toggle:hover { color: var(--text); }

/* Checklist en vivo de reglas de contraseña (alta de Usuario). */
.password-checklist { margin: 6px 0 0; display: flex; flex-direction: column; gap: 4px; }
.regla {
    display: flex; align-items: center; gap: 6px;
    font-size: 0.82rem; color: var(--muted); margin: 0;
}
.regla.regla-ok { color: var(--success); }
.regla-nota { font-size: 0.78rem; color: var(--muted); margin: 2px 0 0; }

/* --- Shell del portal de Staff (header + sidebar + área de trabajo) --- */
.app-shell {
    display: flex; flex-direction: column;
    /* dvh (viewport visible real) en vez de vh (viewport "máximo" con la
       barra de Safari oculta): en iOS, cuando aparece una barra del sistema
       arriba (ej. el aviso "Volver a WhatsApp" al abrir un link desde el
       chat), vh no se achica pero el área visible sí — con solo vh, la
       franja superior del shell (topbar) queda tapada/recortada. */
    height: 100vh; height: 100dvh;
}
.app-body { flex: 1; min-height: 0; display: flex; }

/* --- Menú lateral de Staff (acordeón, colapsable) --- */
.staff-sidebar {
    width: 240px; flex-shrink: 0;
    background: var(--card-bg); border-right: 1px solid var(--border);
    overflow-y: auto; overflow-x: hidden;
    transition: width 0.18s ease;
}
.staff-sidebar.collapsed { width: 56px; }
.sidebar-group-btn {
    width: 100%; background: transparent; border: 0; cursor: pointer;
    display: flex; align-items: center; gap: 10px;
    padding: 11px 14px; font-weight: 600; font-size: 0.88rem; color: var(--text); text-align: left;
}
.sidebar-group-btn:hover { background: var(--accent-soft); }
.sidebar-group-label { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sidebar-caret { font-size: 0.7rem; color: var(--muted); transition: transform 0.18s ease; }
.sidebar-group.open .sidebar-caret { transform: rotate(180deg); }
/* Acordeón suave con altura real (no un max-height "adivinado" que hace que
   un submenú corto termine de revelarse mucho antes de que acabe la
   transición, y se sienta brusco/entrecortado) — técnica de grid con fila
   0fr -> 1fr: el track de la grilla sí anima entre fracciones, a diferencia
   de height/max-height que no puede animar hacia/desde "auto". El contenido
   real vive en .sidebar-group-items-inner, con overflow:hidden y min-height:0
   (si no, una grilla no deja encoger su contenido por debajo de su alto
   natural y el "0fr" no cierra del todo). */
.sidebar-group-items {
    display: grid; grid-template-rows: 0fr; opacity: 0;
    transition: grid-template-rows 0.28s ease, opacity 0.22s ease;
}
.sidebar-group.open .sidebar-group-items { grid-template-rows: 1fr; opacity: 1; }
.sidebar-group-items-inner { overflow: hidden; min-height: 0; }
.sidebar-group-items a {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 14px 8px 42px; font-size: 0.85rem; color: var(--text); text-decoration: none;
}
.sidebar-group-items a::before {
    content: ''; width: 5px; height: 5px; border-radius: 50%;
    background: var(--muted); flex-shrink: 0;
}
.sidebar-group-items a:hover { background: var(--accent-soft); }
.sidebar-group-items a:hover::before { background: var(--accent); }
.staff-sidebar.collapsed .sidebar-group-btn { justify-content: center; padding: 12px 0; }
.staff-sidebar.collapsed .sidebar-group-label,
.staff-sidebar.collapsed .sidebar-caret,
.staff-sidebar.collapsed .sidebar-group-items { display: none; }

/* --- Responsive móvil (portal de Staff): el menú ya arranca cerrado por
   defecto (ver home.html, restoreSidebar) — acá además se ocultan el toggle
   de tema y los indicadores de la barra superior, que no entran cómodos en
   una pantalla angosta. --- */
@media (max-width: 768px) {
    .theme-switch, .topbar-kpi { display: none; }
    /* En desktop, colapsado deja una franja de 56px con solo los íconos
       (modo "rail"). En móvil esa franja sigue robándole ancho a la vista
       activa, así que colapsado acá se oculta del todo (íconos incluidos). */
    .staff-sidebar.collapsed { width: 0; border-right: 0; }
}

/* --- Área de trabajo: barra de pestañas + contenido --- */
.main-area { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.tabs-bar {
    display: flex; align-items: stretch; background: var(--bg-soft); border-bottom: 1px solid var(--border);
    overflow-x: auto;
}
.tab {
    display: flex; align-items: center; gap: 8px; padding: 9px 14px; font-size: 0.82rem; font-weight: 600;
    color: var(--muted); border-right: 1px solid var(--border); cursor: pointer; white-space: nowrap;
    background: transparent;
}
.tab.active { background: var(--card-bg); color: var(--accent-dark); }
.tab .tab-close { opacity: 0.6; font-weight: 400; }
.tab .tab-close:hover { opacity: 1; color: var(--danger); }
.tabs-viewport { position: relative; flex: 1; min-height: 0; background: var(--card-bg); }
.tab-pane { position: absolute; inset: 0; display: none; }
.tab-pane.active { display: block; }
.tab-pane iframe { width: 100%; height: 100%; border: 0; display: block; background: var(--card-bg); }
.tab-empty { display: block; height: 100%; overflow-y: auto; padding: 24px; color: var(--text); }

/* --- Dashboard básico (home del portal de Staff, estado sin pestañas) --- */
.dashboard { max-width: 1100px; margin: 0 auto; }
.dashboard h2 { margin: 0 0 16px; font-size: 1.3rem; display: flex; align-items: center; gap: 8px; }
.dashboard-grids { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 20px; }
@media (max-width: 900px) { .dashboard-grids { grid-template-columns: 1fr; } }
.kpi-estado .kpi-value { font-size: 20px; }

/* --- Visor de esquema ER (Settings) --- */
.erd-toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; position: relative; }
.erd-viewport { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--card-bg);
    height: calc(100vh - 150px); overflow: auto; position: relative; }
.erd-canvas { transform-origin: 0 0; padding: 20px; display: inline-block; }
.erd-canvas .erd-draggable { cursor: grab; touch-action: none; }
.erd-canvas .erd-draggable.dragging { cursor: grabbing; }
.erd-tablas-dropdown { position: relative; }
.erd-tablas-dropdown.open #erd-tablas-toggle { background: var(--accent-soft); border-color: var(--accent); }
.erd-tablas-dropdown .tabla-selector {
    position: absolute; top: calc(100% + 4px); left: 0; z-index: 20; width: 260px;
    max-height: 60vh; overflow-y: auto; border: 1px solid var(--border); border-radius: var(--radius-lg);
    padding: 10px; background: var(--card-bg); box-shadow: var(--shadow-md);
    opacity: 0; visibility: hidden; transform: translateY(-6px);
    transition: opacity 0.15s ease, transform 0.15s ease, visibility 0s linear 0.15s;
    pointer-events: none;
}
.erd-tablas-dropdown.open .tabla-selector {
    opacity: 1; visibility: visible; transform: translateY(0);
    transition: opacity 0.15s ease, transform 0.15s ease;
    pointer-events: auto;
}
.tabla-selector label { display: flex; align-items: center; gap: 8px; padding: 4px 2px; font-size: 0.85rem; cursor: pointer; }

/* --- Overlay de "procesando" al enviar un formulario (evita doble envío,
   ver base_embed.html/base.html) — mismo patrón que shared/DESIGN_SYSTEM.md
   sección "Overlay de procesamiento a pantalla completa". */
.processing-overlay {
    position: fixed; inset: 0; z-index: 9000;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
    background: rgba(0, 0, 0, 0.4); backdrop-filter: blur(1px);
}
.processing-spinner {
    width: 44px; height: 44px; border-radius: 50%;
    border: 4px solid var(--line); border-top-color: var(--brand);
    animation: processing-spin 0.8s linear infinite;
}
.processing-overlay span {
    background: var(--card-bg); color: var(--text);
    padding: 6px 16px; border-radius: var(--radius-pill); font-size: 0.9rem; box-shadow: var(--shadow-md);
}
@keyframes processing-spin { to { transform: rotate(360deg); } }

/* --- Drawer / panel lateral (Ver/Editar/Nuevo/Borrar sin reemplazar toda la
   página) — ver shared/DESIGN_SYSTEM.md sección 18 y
   static/js/panel_lateral.js. z-index por debajo de los modales genuinos
   (.foto-modal en 8000) para que esos sigan abriéndose por encima de un
   panel ya abierto, y por debajo de .processing-overlay (9000) para que un
   spinner de guardado se siga viendo. */
.panel-lateral { position: fixed; inset: 0; z-index: 7800; }
.panel-lateral[hidden] { display: none; }
.panel-lateral-fondo {
    position: absolute; inset: 0;
    background: rgba(0, 0, 0, 0.05);
    cursor: pointer;
}
.panel-lateral-caja {
    position: absolute; top: 0; right: 0; height: 100%;
    width: 50%; max-width: 720px; min-width: min(100%, 420px);
    background: var(--card-bg);
    box-shadow: var(--shadow-md);
    display: flex; flex-direction: column;
    overflow-y: auto;
    padding: 24px;
    transform: translateX(100%);
    transition: transform 0.25s ease;
}
.panel-lateral.abierto .panel-lateral-caja { transform: translateX(0); }
.panel-lateral-cerrar { position: sticky; top: 16px; align-self: flex-end; z-index: 2; margin-bottom: -12px; }
@media (max-width: 768px) {
    .panel-lateral-caja { width: 100%; max-width: none; min-width: 0; }
}
/* Título de la vista actual, congelado al hacer scroll dentro del panel —
   ver shared/DESIGN_SYSTEM.md sección 18 para la explicación del margen/top
   negativos (escapan el padding de .panel-lateral-caja). */
#panel-lateral-body > .page-header:first-child {
    position: sticky; top: -24px;
    margin: -24px -24px 0;
    padding: 24px 56px 16px 24px;
    background: var(--card-bg);
    z-index: 1;
}
