@charset "UTF-8";
/*
 * SKA Admin Components — Centralized CSS
 *
 * RULE: Zero hardcoded colors. Only var(--ska-*) from ska-tokens.css.
 * Location: settings/static/settings/css/
 * Loaded per-template via {% static 'settings/css/ska-admin-components.css' %}
 *
 * Components:
 * - .ska-dashboard, .ska-dash-* — KPI cards & dashboard grids
 * - .ska-stat-* — Stat cards (LLM usage, etc.)
 * - .ska-data-table — Clean data tables
 * - .ska-badge-* — Status badges
 * - .gcm-* — Google Credentials Manager
 * - .dsf-* — Drive Sync Filter dashboard
 * - .mcp-* — MCP Tester
 * - Utility classes (.ska-text-right, etc.)
 */

/* ═══════════════════════════════════════════════════
   TAILWIND-LIKE DESIGN TOKENS (Safe Mapping)
   ═══════════════════════════════════════════════════ */
.bg-ska-surface {
    background: var(--ska-surface) !important;
}

.bg-ska-surface-alt {
    background: var(--ska-surface-alt) !important;
}

.border-ska-border {
    border-color: var(--ska-border) !important;
}

.border-ska-primary {
    border-color: var(--ska-primary) !important;
}

.text-ska-text {
    color: var(--ska-text) !important;
}

.text-ska-text-secondary {
    color: var(--ska-text-secondary) !important;
}

.text-ska-text-muted {
    color: var(--ska-text-muted) !important;
}

.text-ska-text-subtle {
    color: var(--ska-text-subtle) !important;
}

/* El color de marca ESCRITO. Consume `--ska-primary-text` y no `--ska-primary`: el
   segundo está afinado para usarse de FONDO y como letra sobre una superficie clara no
   llega a 4,5:1 en cuatro de las cinco marcas (petnova 2,26 · javiermartinez 2,31 ·
   taller-kids 2,59 · prismo_so 2,76, sobre el fondo de página). El token nuevo se hunde
   0,35 en claro y deja el peor caso en 4,94; en oscuro vale lo mismo que el primario,
   que ahí ya pasa. El porqué del número está en `ThemeEngine`, donde se calcula.

   Esta regla la usan 128 templates. Cambiarla acá los alcanza a los 128 sin tocar uno. */
.text-ska-primary {
    color: var(--ska-primary-text) !important;
}

/* La letra que el motor calcula PARA el fondo de marca (`ThemeEngine.texto_sobre`).
   Faltaba: el token `--ska-text-on-primary` existe en los dos modos desde que se
   arregló la legibilidad sobre la marca, pero no tenía clase acá, así que una
   plantilla que quisiera pintar algo con fondo `bg-ska-primary` no tenía con qué
   escribirle encima. Escribir la clase sin este mapeo no da error: Django la
   emite, el navegador no la conoce y el color simplemente no se aplica. */
.text-ska-text-on-primary {
    color: var(--ska-text-on-primary) !important;
}

/* ── Mappings Extraídos de UI_COMPONENTS.md ── */
.text-ska-success {
    color: var(--ska-success) !important;
}

.text-ska-danger {
    color: var(--ska-danger) !important;
}

.text-ska-warning {
    color: var(--ska-warning) !important;
}

.text-ska-info {
    color: var(--ska-info) !important;
}

.bg-ska-primary {
    background-color: var(--ska-primary) !important;
}

.bg-ska-primary-dark {
    background-color: var(--ska-primary-dark) !important;
}

.hover\:text-ska-primary:hover {
    color: var(--ska-primary-text) !important;
}

.hover\:border-ska-primary:hover {
    border-color: var(--ska-primary) !important;
}

.hover\:bg-ska-surface:hover {
    background-color: var(--ska-surface) !important;
}

.hover\:bg-ska-surface-alt:hover {
    background-color: var(--ska-surface-alt) !important;
}

.hover\:bg-ska-primary-dark:hover {
    background-color: var(--ska-primary-dark) !important;
}

/* ═══════════════════════════════════════════════════
   NATIVE <dialog>:modal CENTERING — preflight restoration
   ═══════════════════════════════════════════════════
   Tailwind preflight (unfold/css/styles.css) declares
   `*,::backdrop{margin:0}` (author origin), which OVERRIDES the browser UA
   stylesheet rule `dialog:modal{margin:auto}` that centers modal dialogs.
   Effect: every <dialog>.showModal() sticks to the top-left corner
   (UA `:modal` gives position:fixed + inset:0, but margin is now 0 instead
   of auto). Restoring margin:auto re-centers ALL native modal dialogs
   project-wide. `dialog:modal` (specificity 0,1,1) beats `*` (0,0,0), so
   this wins regardless of stylesheet load order. */
dialog:modal {
    margin: auto;
}

/* ═══════════════════════════════════════════════════
   UNIFIED DASHBOARD & DETAIL VIEWS (ska-admin-*)
   Official standard as per DESIGN_SYSTEM.md Section 8
   ═══════════════════════════════════════════════════ */

/* ── Layout — full-width (match Flutter scaffold).
       El content area Unfold ya provee padding lateral; aquí solo padding vertical. ── */
.ska-admin-wrap {
    width: 100%;
    padding: 16px 0;
    box-sizing: border-box;
}

/* ── Page Header ── */
.ska-admin-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
}

.ska-admin-header h1 {
    font-size: 22px;
    font-weight: 700;
    color: var(--ska-text);
    margin: 0;
    display: flex;
    align-items: center;
    gap: 10px;
}

.ska-admin-header h1 i {
    color: var(--ska-primary);
}

/* ── Nav Lateral / Tabs ── */
.ska-admin-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 12px 0 20px;
    border-bottom: 1px solid var(--ska-border);
    margin-bottom: 24px;
}

.ska-admin-nav-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    border-radius: var(--ska-radius-md);
    font-size: 13px;
    font-weight: 500;
    color: var(--ska-text-secondary);
    background: var(--ska-surface);
    border: 1px solid var(--ska-border);
    text-decoration: none;
    transition: all 0.2s ease;
}

.ska-admin-nav-link:hover {
    color: var(--ska-text);
    background: var(--ska-surface-alt);
    border-color: var(--ska-border-hover);
    box-shadow: var(--ska-shadow-sm);
}

.ska-admin-nav-link.active {
    color: var(--ska-primary);
    background: var(--ska-surface-alt);
    border-color: var(--ska-primary);
    font-weight: 600;
}

.ska-admin-nav-link i {
    text-align: center;
    width: 1rem;
}

/* ── Breadcrumbs Unificados ── */
.ska-admin-breadcrumb {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.625rem 1rem;
    margin-bottom: 1.5rem;
    font-size: 13px;
    font-weight: 500;
    width: 100%;
    background-color: var(--ska-surface-alt);
    border-radius: var(--ska-radius-md);
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    /* shadow-sm equivalente */
    border: 1px solid var(--ska-border);
    color: var(--ska-text-muted);
}

.ska-admin-breadcrumb-link {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    color: var(--ska-text-muted);
    transition: color 0.2s ease-in-out;
    text-decoration: none;
}

.ska-admin-breadcrumb-link:hover {
    color: var(--ska-primary);
}

.ska-admin-breadcrumb-separator {
    color: var(--ska-border-medium);
    font-size: 9px;
    display: flex;
    align-items: center;
}

.ska-admin-breadcrumb-current {
    color: var(--ska-text);
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── App Dock (Bento Navigation) ── */
.ska-admin-dock-nav {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
    gap: 16px;
    margin-bottom: 32px;
}

.ska-admin-dock-btn {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    background: var(--ska-surface);
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-md);
    text-decoration: none;
    font-size: 14px;
    font-weight: 600;
    color: var(--ska-text);
    transition: all 0.2s ease;
    box-shadow: var(--ska-shadow-sm);
    margin-top: 2px;
}

.ska-admin-dock-btn:hover {
    background: var(--ska-surface-alt);
    border-color: var(--ska-border-hover);
    box-shadow: var(--ska-shadow-md);
    transform: translateY(-1px);
}

.ska-admin-dock-btn .dock-icon {
    width: 34px;
    height: 34px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    font-size: 16px;
}

.ska-admin-dock-btn .dock-icon.color-green {
    background: var(--ska-badge-green-bg);
    color: var(--ska-accent-green);
}

.ska-admin-dock-btn .dock-icon.color-gray {
    background: var(--ska-surface-alt);
    color: var(--ska-text-secondary);
    border: 1px solid var(--ska-border);
}

.ska-admin-dock-btn .dock-icon.color-amber {
    background: var(--ska-badge-yellow-bg);
    color: var(--ska-accent-amber);
}

.ska-admin-dock-btn .dock-icon.color-blue {
    background: var(--ska-badge-blue-bg);
    color: var(--ska-accent-blue);
}

.ska-admin-dock-btn .dock-icon.color-purple {
    background: var(--ska-purple-bg);
    color: var(--ska-accent-purple);
}

.ska-admin-dock-btn .dock-icon.color-red {
    background: var(--ska-badge-red-bg);
    color: var(--ska-accent-red);
}

/* ── Filter Bar ── */
.ska-admin-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    padding: 14px 16px;
    background: var(--ska-surface);
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-md);
    margin-bottom: 20px;
    align-items: flex-end;
}

.ska-admin-filter-group {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.ska-admin-filter-group label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--ska-text-muted);
}

.ska-admin-filter-group select,
.ska-admin-filter-group input {
    padding: 6px 10px;
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-sm);
    background: var(--ska-bg);
    color: var(--ska-text);
    font-size: 13px;
    min-width: 140px;
}

.ska-admin-filter-group select:focus,
.ska-admin-filter-group input:focus {
    outline: none;
    border-color: var(--ska-primary);
    box-shadow: 0 0 0 2px var(--ska-primary-glow);
}

.ska-admin-filter-btn {
    padding: 6px 16px;
    background: var(--ska-primary);
    color: var(--ska-text-on-primary);
    border: none;
    border-radius: var(--ska-radius-sm);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.2s;
    align-content: center;
}

.ska-admin-filter-btn:hover {
    background: var(--ska-primary-dark);
}

.ska-admin-filter-btn-reset {
    padding: 8.5px 16px;
    background: transparent;
    color: var(--ska-text-muted);
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-sm);
    font-size: 13px;
    cursor: pointer;
    transition: all 0.2s;
    align-content: center;
}

.ska-admin-filter-btn-reset:hover {
    color: var(--ska-danger);
    border-color: var(--ska-danger);
}

/* ── Global Data Table (replaces ik-admin-table) ── */
.ska-admin-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-md);
    overflow: hidden;
}

.ska-admin-table thead th {
    padding: 10px 14px;
    text-align: left;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--ska-text-muted);
    background: var(--ska-surface-alt);
    border-bottom: 1px solid var(--ska-border);
}

.ska-admin-table tbody tr {
    transition: background 0.15s;
}

.ska-admin-table tbody tr:hover {
    background: var(--ska-surface-alt);
}

.ska-admin-table tbody td,
.ska-admin-table tbody th {
    /* tbody th = cabecera de FILA (key-value): mismas guías que td */
    padding: 10px 14px;
    font-size: 13px;
    color: var(--ska-text);
    border-bottom: 1px solid var(--ska-border);
    vertical-align: middle;
}

.ska-admin-table tbody th {
    text-align: left;
    /* th por defecto centra → forzar izquierda */
    font-weight: 600;
}

.ska-admin-table tbody tr:last-child td,
.ska-admin-table tbody tr:last-child th {
    border-bottom: none;
}

/* EL PIE. No existía ninguna regla `tfoot` en todo el CSS fuente del proyecto, así que
   la fila del total caía al valor por omisión del navegador —que es CERO— y quedaba
   aplastada contra el borde, al lado de un encabezado y unas filas que sí tienen aire.
   Reportado por Sergio el 21-ago-2026 sobre el cierre de turno de Petnova: «Espacio de
   "Total $732.000", "Entradas − salidas $140.000 − $75.000" super pequeño comparado con
   el resto».

   El aire es EL MISMO de `tbody` a propósito: el pie es la última fila de la tabla, no
   otro componente. Si algún día el de las filas cambia, este tiene que cambiar con él —
   y hay un test que se pone rojo si los dos dejan de coincidir.

   El `border-top` es lo que lo hace leer como CIERRE: `tbody tr:last-child` se queda sin
   su borde inferior a propósito (regla de arriba), así que sin esta línea el total se
   pega a la última fila sin ninguna separación. */
.ska-admin-table tfoot td,
.ska-admin-table tfoot th {
    padding: 10px 14px;
    font-size: 13px;
    color: var(--ska-text);
    vertical-align: middle;
    border-top: 1px solid var(--ska-border);
}

.ska-admin-table--flush {
    /* Tabla que vive DENTRO de un panel ya bordeado (card con header propio):
       el panel aporta el borde + radio; la tabla NO debe duplicarlos (evita el
       efecto "tabla dentro de tabla"). Conserva separadores de fila y thead. */
    border: none;
    border-radius: 0;
}

/* ── Ledger agrupado por movimiento (changelist de Transaction) ──────────────
   Un `<tbody>` por movimiento: el elemento NATIVO para un grupo de filas, con
   `role=rowgroup` implícito. MDN pide no reimplementar con ARIA lo que el HTML
   ya hace, así que acá no hay ningún `role=` inventado — sólo estilo.

   ⚠️ Lo primero que hay que saber de este bloque: `.ska-admin-table tbody
   tr:last-child { border-bottom: none }` (arriba) se aplica POR `<tbody>`. Con
   varios, cada grupo perdía la línea de su última fila y los movimientos se
   pegaban entre sí. Se devuelve acá, y el separador FUERTE pasa a ser el borde
   del `<tbody>`, que es lo que de verdad separa un movimiento del siguiente. */
.ska-ledger-table tbody.ska-ledger-group:not(:last-child) {
    border-bottom: 2px solid var(--ska-border);
}

.ska-ledger-table tbody.ska-ledger-group tr:last-child td {
    border-bottom: 1px solid var(--ska-border);
}

.ska-ledger-group__head {
    background: var(--ska-surface-alt);
    cursor: pointer;
    user-select: none;
}

.ska-ledger-group__head:hover {
    background: var(--ska-surface-alt);
    filter: brightness(0.98);
}

.ska-ledger-group__head:focus-visible {
    outline: 2px solid var(--ska-primary);
    outline-offset: -2px;
}

.ska-ledger-group__head > td {
    padding: 8px 14px !important;
}

.ska-ledger-group__bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.ska-ledger-group__chevron {
    font-size: 11px;
    color: var(--ska-text-subtle);
    transition: transform 0.15s ease;
}

.ska-ledger-group__chevron.is-open {
    transform: rotate(90deg);
}

.ska-ledger-group__title {
    font-weight: 700;
    font-size: 13px;
    color: var(--ska-text);
}

/* Con quién fue el movimiento. Pesa menos que el título —el título es qué hecho es— pero más
   que la fecha: es lo que el operador busca cuando barre la lista con la vista. */
.ska-ledger-group__party {
    font-size: 12px;
    color: var(--ska-text-subtle);
    font-weight: 500;
}

.ska-ledger-group__date {
    font-size: 11px;
    color: var(--ska-text-subtle);
    text-transform: capitalize;
}

/* Los números del movimiento se van a la derecha; en pantalla angosta el
   `flex-wrap` del bar los baja a su propia línea en vez de comprimir el título. */
.ska-ledger-group__numbers {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    font-size: 12px;
    white-space: nowrap;
}

/* La fila de un asiento va un punto más tenue que su cabecera: el ojo agarra
   primero el movimiento y después sus partes. */
.ska-ledger-leg > td {
    font-size: 12.5px;
}

.ska-ledger-summary {
    display: flex;
    gap: 1.25rem;
    flex-wrap: wrap;
    align-items: center;
    margin-bottom: 0.75rem;
    padding: 0.6rem 0.9rem;
    background: var(--ska-surface-alt);
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-md);
    font-size: 13px;
}

/* La fila desde la que se llegó tras la redirección satélite → principal. Sin una marca, el
   operador hace click en #24743, aterriza en #24741 y no tiene forma de saber por qué la
   pantalla cambió ni dónde quedó el asiento que abrió. */
.ska-ledger-leg--from > td {
    background: var(--ska-surface-alt);
    box-shadow: inset 3px 0 0 0 var(--ska-primary);
}

.ska-ledger-summary__title {
    font-weight: 700;
    text-transform: uppercase;
    font-size: 11px;
    letter-spacing: 0.5px;
    color: var(--ska-text-subtle);
}

/* ── KPI Cards ── */
.ska-admin-kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px;
    margin-bottom: 24px;
}

.ska-admin-kpi-card {
    padding: 16px 20px;
    background: var(--ska-surface);
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-md);
    transition: box-shadow 0.2s;
}

.ska-admin-kpi-card:hover {
    box-shadow: var(--ska-shadow-md);
}

/* ``.kpi-*`` sirven tanto DENTRO de ``.ska-admin-kpi-card`` (grid de KPIs) como SUELTAS
   (p.ej. el Pulso del Home, que apila los números en una lista dividida sin card por número). */
/* ── Dona de dinero ──────────────────────────────────────────────────────────
   Gráfico de torta sin librería: la CSP del admin no carga scripts externos, y una dona
   con agujero son dos cajas y un `conic-gradient`. Los cortes los inyecta el template en
   `--ska-donut-stops` (ver _money_card.html), ya calculados como % ENTEROS: un float ahí
   se serializa con COMA bajo es-CO y el navegador descarta la regla → la dona desaparece
   sólo en español. El color de cada quesito viaja en el stop, no en una var por hijo:
   las custom properties heredan hacia abajo y el padre no vería las de sus hijos. */
.ska-donut {
    position: relative;
    flex: 0 0 auto;
    width: 104px;
    height: 104px;
    border-radius: 50%;
    background: conic-gradient(var(--ska-donut-stops, var(--ska-border) 0% 100%));
}

.ska-donut__hole {
    position: absolute;
    inset: 21%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 4px;
    border-radius: 50%;
    background: var(--ska-surface);
    text-align: center;
}

.ska-donut__total {
    font-size: 12px;
    font-weight: 700;
    line-height: 1.15;
    color: var(--ska-text);
    /* El total lleva moneda y miles ("$1.250.000"): sin esto rebalsa el agujero. */
    overflow-wrap: anywhere;
}

/* Cuadradito de color que ata cada fila de la leyenda a su quesito. */
.ska-donut__key {
    display: inline-block;
    width: 8px;
    height: 8px;
    margin-right: 6px;
    border-radius: 2px;
    vertical-align: middle;
}

.kpi-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--ska-text-muted);
    margin-bottom: 6px;
    display: flex;
    align-items: center;
    gap: 6px;
}

.ska-admin-kpi-card .kpi-value,
.kpi-value {
    font-size: 30px;
    font-weight: 700;
    color: var(--ska-text);
    line-height: 1.05;
    letter-spacing: -0.01em;
    font-variant-numeric: tabular-nums;
}

/* Fila número + curva. La usan las cards que traen serie (sparkline SVG); las que
   no, siguen apilando kpi-value normal. `--kpi-accent` (inline por card) tiñe la curva. */
.ska-admin-kpi-card .kpi-row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 12px;
    min-height: 32px;
}
.ska-admin-kpi-card .kpi-spark {
    flex: 0 0 auto;
    width: 92px;
    height: 30px;
    overflow: visible;
    color: var(--kpi-accent, var(--ska-primary));
}
.ska-admin-kpi-card .kpi-spark polyline {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    vector-effect: non-scaling-stroke;
}
.ska-admin-kpi-card .kpi-spark circle {
    fill: currentColor;
}

.ska-admin-kpi-card .kpi-sub,
.kpi-sub {
    font-size: 12px;
    color: var(--ska-text-secondary);
    margin-top: 4px;
}

/* ── Custom Detail Views (Detail Grid) ── */
.ska-admin-detail-grid {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 20px;
    margin-bottom: 24px;
}

@media (max-width: 900px) {
    .ska-admin-detail-grid {
        grid-template-columns: 1fr;
    }
}

/* Columnas del split 2:1 (grid children). Regla compartida del platform admin:
   min-width:0 evita el overflow de grid items con contenido ancho (grids de
   fotos, texto largo); flex+gap apila las secciones con espaciado consistente
   sin depender del margin de cada sección. */
.ska-admin-detail-column-main,
.ska-admin-detail-column-side {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.ska-admin-detail-section {
    background: var(--ska-surface);
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-md);
    padding: 20px;
    margin-bottom: 20px;
}

.ska-admin-detail-section h3 {
    font-size: 14px;
    font-weight: 700;
    color: var(--ska-text);
    margin: 0;
    display: flex;
    align-items: center;
    gap: 8px;
}

.ska-admin-detail-section h3 i {
    color: var(--ska-primary);
}

.ska-admin-detail-header {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    background: var(--ska-surface);
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-md);
    padding: 16px 20px;
    margin-bottom: 20px;
}

.ska-admin-detail-icon {
    font-size: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 60px;
    height: 60px;
    background: var(--ska-surface-alt);
    border-radius: var(--ska-radius-md);
    color: var(--ska-text-muted);
}

.ska-admin-detail-title {
    font-size: 22px;
    font-weight: 700;
    margin: 0 0 6px 0;
    color: var(--ska-text);
}

/* ── Universal Tabs ── */
.ska-admin-tabs {
    display: flex;
    gap: 0;
    margin-bottom: 20px;
    border-bottom: 2px solid var(--ska-border);
}

.ska-admin-tab {
    padding: 10px 20px;
    font-size: 13px;
    font-weight: 600;
    color: var(--ska-text-muted);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    transition: all 0.2s;
    display: flex;
    align-items: center;
    gap: 6px;
}

.ska-admin-tab:hover {
    color: var(--ska-text);
}

.ska-admin-tab.active {
    color: var(--ska-primary);
    border-bottom-color: var(--ska-primary);
}

.ska-admin-tab .tab-count {
    background: var(--ska-border);
    color: var(--ska-text-muted);
    padding: 1px 6px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
}

.ska-admin-tab.active .tab-count {
    background: var(--ska-primary);
    color: var(--ska-text-on-primary);
}

/* ── JSON Block ── */
.ska-admin-json-block {
    background: var(--ska-bg);
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-md);
    padding: 12px;
    font-family: var(--ska-font-mono, monospace);
    font-size: 12px;
    color: var(--ska-text);
    overflow-x: auto;
    white-space: pre-wrap;
    word-break: break-all;
    margin: 0;
}

/* ═══════════════════════════════════════════════════
   DASHBOARD (Admin Dashboard)
   ═══════════════════════════════════════════════════ */

.ska-dashboard,
.ik-chat-dashboard {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}

.ska-dash-grid,
.dash-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 20px;
    margin-bottom: 30px;
}

.ska-dash-card,
.dash-card {
    background: var(--ska-surface);
    border: 1px solid var(--ska-border-medium);
    border-radius: var(--ska-radius-md);
    padding: 20px;
    box-shadow: var(--ska-shadow-sm);
}

.ska-dash-card h3,
.dash-card h3 {
    margin: 0 0 4px 0;
    font-size: 14px;
    color: var(--ska-text-muted);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.ska-dash-card .value,
.dash-card .value {
    font-size: 32px;
    font-weight: 700;
    color: var(--ska-text);
}

.ska-dash-card .sub,
.dash-card .sub {
    font-size: 13px;
    color: var(--ska-text-subtle);
    margin-top: 4px;
}

.ska-dash-section,
.dash-section {
    margin-bottom: 30px;
}

.ska-dash-section h2,
.dash-section h2 {
    font-size: 18px;
    font-weight: 600;
    margin: 0 0 15px 0;
    padding-bottom: 8px;
    border-bottom: 2px solid var(--ska-primary);
}

/* ═══════════════════════════════════════════════════
   DATA TABLES (shared by dashboard, llmusage, etc.)
   ═══════════════════════════════════════════════════ */

.ska-data-table,
.dash-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

.ska-data-table th,
.dash-table th {
    text-align: left;
    padding: 10px 12px;
    background: var(--ska-surface-alt);
    font-weight: 600;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ska-text-muted);
}

.ska-data-table td,
.dash-table td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--ska-border);
}

.ska-data-table tr:hover td,
.dash-table tr:hover td {
    background: var(--ska-surface-alt);
}

/* ═══════════════════════════════════════════════════
   BADGES
   ═══════════════════════════════════════════════════ */

.ska-badge,
[class^="ska-badge-"],
[class*=" ska-badge-"],
.badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    /* Separación consistente entre iconos y texto */
    padding: 0.125rem 0.5rem;
    /* px-2 py-0.5 fallback */
    border-radius: 9999px;
    font-size: 0.75rem;
    /* 12px text-xs */
    font-weight: 500;
    white-space: nowrap;
    /* Previene rupturas de línea inestéticas */
    line-height: 1.25rem;
}

.ska-badge-green,
.badge-green {
    background: var(--ska-badge-green-bg);
    color: var(--ska-badge-green-text);
}

/* `success` es ALIAS de `green`, y que el alias FALTARA era el defecto: la clase se venía usando en
   8 lugares —el premio de fidelización en cuatro pantallas de petnova, el turno cerrado en dos de
   arqueo— y NO tenía regla de fondo en ningún CSS del repo (medido: 0 coincidencias, incluido el
   estático colectado). El selector base `[class*=" ska-badge-"]` sí la agarraba, así que el chip
   recibía forma, padding y radio pero heredaba el color de su celda: se pintaba, se veía mal, y
   nada se quejaba — el HTML sale exactamente igual con regla y sin ella.

   NO se inventa un verde nuevo, a propósito. El par `--ska-badge-green-*` ya vive en las dos
   paletas (claro y oscuro) y ya se pinta en estas mismas tablas —es el badge de "Confirmada" de la
   agenda—, así que reusarlo hereda un color ya validado en vez de abrir una deuda de contraste el
   día que nace.

   ALCANCE, porque esta hoja la sirve UNFOLD["STYLES"] en TODO el admin: los ~25 `ik-badge
   ska-badge-success` de interak también pasan a tener fondo. Hoy no lo tienen —`interak_hub.css`
   define `.ik-badge-success` con guion `ik-`, no `ska-`— así que ese marcado ya pedía un verde y no
   lo recibía. Es la misma falla, en otra pantalla, y se arregla por el mismo lado. */
.ska-badge-success,
.badge-success {
    background: var(--ska-badge-green-bg);
    color: var(--ska-badge-green-text);
}

.ska-badge-red,
.badge-red {
    background: var(--ska-badge-red-bg);
    color: var(--ska-badge-red-text);
}

.ska-badge-yellow,
.badge-yellow {
    background: var(--ska-badge-yellow-bg);
    color: var(--ska-badge-yellow-text);
}

.ska-badge-blue,
.badge-blue {
    background: var(--ska-badge-blue-bg);
    color: var(--ska-badge-blue-text);
}

.ska-badge-gray,
.badge-gray {
    background: var(--ska-surface-alt);
    color: var(--ska-text-muted);
}

/* ═══════════════════════════════════════════════════
   FLAG ROWS (feature flags in dashboard)
   ═══════════════════════════════════════════════════ */

.ska-flag-row,
.flag-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 0;
    border-bottom: 1px solid var(--ska-border);
}

.ska-flag-name,
.flag-name {
    font-family: var(--ska-font-mono);
    font-size: 13px;
    font-weight: 600;
    min-width: 320px;
}

.ska-flag-value,
.flag-value {
    font-size: 13px;
}

/* ═══════════════════════════════════════════════════
   STAT CARDS (LLM Usage)
   ═══════════════════════════════════════════════════ */

.ska-stat-dashboard,
.llm-dashboard {
    margin-bottom: 24px;
}

.ska-stat-grid,
.llm-stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 16px;
    margin-bottom: 24px;
}

.ska-stat-card,
.llm-stat-card {
    border-radius: var(--ska-radius-md);
    padding: 16px;
    text-align: center;
    border: 1px solid var(--ska-border);
    background: var(--ska-surface);
}

.ska-stat-value,
.llm-stat-value {
    font-size: 28px;
    font-weight: 700;
}

.ska-stat-label,
.llm-stat-label {
    font-size: 12px;
    margin-top: 4px;
    color: var(--ska-text-muted);
}

.ska-section-title,
.llm-section-title {
    cursor: pointer;
    font-weight: 600;
    font-size: 14px;
    padding: 8px 0;
    color: var(--ska-text-secondary);
}

.ska-stat-table,
.llm-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}

.ska-stat-table thead tr,
.llm-table thead tr {
    border-bottom: 2px solid var(--ska-border-medium);
}

.ska-stat-table th,
.llm-table th {
    padding: 10px 12px;
    color: var(--ska-text-muted);
}

.ska-stat-table td,
.llm-table td {
    padding: 8px 12px;
}

.ska-stat-table tbody tr,
.llm-table tbody tr {
    border-bottom: 1px solid var(--ska-border);
}

/* ── Current shift row highlight ── */
.llm-row-current {
    background: var(--ska-primary-glow);
    font-weight: 600;
}

.llm-row-current td:first-child::before {
    content: "▶ ";
}

/* ═══════════════════════════════════════════════════
   GOOGLE CREDENTIALS MANAGER
   ═══════════════════════════════════════════════════ */

.gcm-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}

.gcm-section {
    margin-bottom: 32px;
}

.gcm-section-title {
    font-size: 18px;
    font-weight: 600;
    margin: 0 0 16px 0;
    padding-bottom: 8px;
    border-bottom: 2px solid var(--ska-primary);
    color: var(--ska-text);
}

.gcm-back-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 20px;
    font-size: 13px;
    color: var(--ska-accent);
    text-decoration: none;
}

.gcm-back-link:hover {
    text-decoration: underline;
}

/* ── Cards ── */
.gcm-card {
    background: var(--ska-surface);
    border: 1px solid var(--ska-border-medium);
    border-radius: var(--ska-radius-md);
    padding: 24px;
    box-shadow: var(--ska-shadow-sm);
    margin-bottom: 16px;
}

.gcm-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
}

.gcm-card-title {
    font-size: 16px;
    font-weight: 600;
    color: var(--ska-text);
    display: flex;
    align-items: center;
    gap: 8px;
}

.gcm-card-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.gcm-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
    gap: 16px;
}

/* ── GCM Badges ── */
.gcm-badge {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 12px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.4;
}

.gcm-badge-valid {
    background: var(--ska-success-bg);
    color: var(--ska-success);
}

.gcm-badge-expired {
    background: var(--ska-danger-bg);
    color: var(--ska-danger);
}

.gcm-badge-expiring {
    background: var(--ska-warning-bg);
    color: var(--ska-warning);
}

.gcm-badge-none {
    background: var(--ska-surface-alt);
    color: var(--ska-text-muted);
}

.gcm-badge-oauth {
    background: var(--ska-info-bg);
    color: var(--ska-info);
}

.gcm-badge-sa {
    background: var(--ska-purple-bg);
    color: var(--ska-purple);
}

/* ── GCM Info rows ── */
.gcm-info-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    border-bottom: 1px solid var(--ska-border);
}

.gcm-info-row:last-child {
    border-bottom: none;
}

.gcm-info-label {
    font-size: 13px;
    color: var(--ska-text-muted);
    min-width: 120px;
    font-weight: 500;
}

.gcm-info-value {
    font-size: 13px;
    color: var(--ska-text);
}

/* ── Sync chips ── */
.gcm-sync-grid {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

.gcm-sync-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border-radius: 8px;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
    transition: opacity 0.15s ease;
}

.gcm-sync-chip:hover {
    opacity: 0.8;
}

.gcm-sync-on {
    background: var(--ska-success-bg);
    color: var(--ska-success);
}

.gcm-sync-off {
    background: var(--ska-surface-alt);
    color: var(--ska-text-subtle);
    text-decoration: line-through;
}

/* ── GCM Buttons ── */
.gcm-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 6px 14px;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    border: 1px solid transparent;
    transition: all 0.15s ease;
}

.gcm-btn-primary {
    background: var(--ska-primary);
    color: var(--ska-text-on-primary);
}

.gcm-btn-primary:hover {
    opacity: 0.9;
    color: var(--ska-surface);
}

.gcm-btn-danger {
    background: var(--ska-danger-bg);
    color: var(--ska-danger);
    border-color: var(--ska-danger-bg);
}

.gcm-btn-danger:hover {
    opacity: 0.85;
}

.gcm-btn-sm {
    padding: 0.25rem 0.6rem;
    font-size: 0.75rem;
}

.gcm-input-inline {
    padding: 0.3rem 0.5rem;
    font-size: 0.85rem;
    border: 1px solid var(--ska-border-medium);
    border-radius: var(--ska-radius);
    background: var(--ska-surface-alt);
    color: var(--ska-text);
    width: 140px;
}

.gcm-input-inline:focus {
    outline: none;
    border-color: var(--ska-accent);
}

.gcm-btn-secondary {
    background: var(--ska-surface-alt);
    color: var(--ska-text);
    border-color: var(--ska-border-medium);
}

.gcm-btn-secondary:hover {
    background: var(--ska-border-medium);
}

/* ── Contrato de overlay ─────────────────────────────────────────────────────
   Un overlay a pantalla completa DEBE poder scrollear y NO puede centrar con
   `align-items:center`. Con un hijo más alto que la pantalla, el centrado reparte
   espacio libre NEGATIVO en DOS: recorta arriba y abajo a la vez, y el navegador no
   scrollea por encima del borde inicial → el título y los botones quedan
   INALCANZABLES (no solo feos). Fue el bug del modal de reservas de jul-2026: la
   clienta no podía guardar la cita porque el botón nacía fuera de la pantalla.
   El centrado se hace con `margin:auto` en la tarjeta: cuando no hay espacio libre
   colapsa a 0 (Flexbox §9.6) y apoya la tarjeta arriba en vez de recortarla.

   Son clases CSS propias y no utilidades Tailwind A PROPÓSITO: el build de Unfold
   pasa por PurgeCSS y `m-auto` no sobrevive (verificado contra el styles.css del build).
   Lo hace cumplir scripts/check_modal_scroll.py. */
.ska-modal-scroll {
    overflow-y: auto;
    overscroll-behavior: contain;
}

.ska-modal-card {
    margin: auto;
}

/* ── GCM Inline Confirm Modal ── */
.gcm-confirm-overlay {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.65);
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--ska-radius);
    z-index: 50;
    backdrop-filter: blur(2px);
}

.gcm-confirm-box {
    background: var(--ska-surface);
    border: 1px solid var(--ska-border-medium);
    border-radius: var(--ska-radius);
    padding: 1.5rem 2rem;
    text-align: center;
    max-width: 320px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}

.gcm-confirm-icon {
    font-size: 2rem;
    margin-bottom: 0.5rem;
}

.gcm-confirm-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--ska-text);
    margin-bottom: 0.4rem;
}

.gcm-confirm-text {
    font-size: 0.82rem;
    color: var(--ska-text-dim);
    margin-bottom: 1rem;
    line-height: 1.4;
}

.gcm-confirm-actions {
    display: flex;
    gap: 0.75rem;
    justify-content: center;
}

.gcm-confirm-actions .gcm-btn {
    min-width: 100px;
}

/* ── GCM Username Form ── */
.gcm-username-form {
    display: inline-flex;
    gap: 0.35rem;
    align-items: center;
}

.gcm-btn-save {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--ska-radius);
    border: 1px solid var(--ska-accent);
    background: var(--ska-accent);
    color: var(--ska-text-on-primary);
    font-size: 0.9rem;
    font-weight: bold;
    cursor: pointer;
    transition: opacity 0.15s;
    padding: 0;
}

.gcm-btn-save:hover {
    opacity: 0.85;
}

/* Card needs position:relative for the confirm overlay */
.gcm-card {
    position: relative;
}

/* ── GCM Empty state ── */
.gcm-empty {
    text-align: center;
    padding: 40px 24px;
    color: var(--ska-text-subtle);
}

.gcm-empty-icon {
    font-size: 48px;
    margin-bottom: 12px;
}

.gcm-empty-text {
    font-size: 14px;
    margin-bottom: 16px;
}

/* ── Changelist banner ── */
.gcm-changelist-banner {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    margin-bottom: 16px;
    border-radius: var(--ska-radius-md);
    background: var(--ska-primary-glow);
    border: 1px solid var(--ska-primary-glow);
}

.gcm-manage-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 16px;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    background: var(--ska-primary);
    color: var(--ska-text-on-primary);
    transition: opacity 0.15s ease;
}

.gcm-manage-btn:hover {
    opacity: 0.9;
    color: var(--ska-surface);
}

.gcm-manage-hint {
    font-size: 13px;
    color: var(--ska-text-muted);
}

/* ═══════════════════════════════════════════════════
   MCP TESTER
   ═══════════════════════════════════════════════════ */

.mcp-tester-body {
    font-family: var(--ska-font-sans);
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
    line-height: 1.6;
    background: var(--ska-bg);
    color: var(--ska-text);
}

.mcp-tester-body h1 {
    color: var(--ska-text);
    text-align: center;
    margin-bottom: 30px;
}

.mcp-container {
    display: flex;
    gap: 20px;
}

.mcp-panel {
    flex: 1;
    border: 1px solid var(--ska-border-medium);
    border-radius: var(--ska-radius-sm);
    padding: 15px;
    background: var(--ska-surface);
}

.mcp-tester-body textarea {
    width: 100%;
    height: 300px;
    font-family: var(--ska-font-mono);
    padding: 10px;
    box-sizing: border-box;
    border: 1px solid var(--ska-border-medium);
    border-radius: 4px;
    margin-bottom: 15px;
    background: var(--ska-surface);
    color: var(--ska-text);
}

.mcp-tester-body button {
    background-color: var(--ska-primary);
    color: var(--ska-text-on-primary);
    padding: 10px 15px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 16px;
}

.mcp-tester-body button:hover {
    background-color: var(--ska-primary-dark);
}

.mcp-examples {
    margin-top: 20px;
}

.mcp-example-btn {
    background-color: var(--ska-info);
    color: var(--ska-surface);
    margin-right: 10px;
    margin-bottom: 10px;
    padding: 10px 15px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 16px;
}

.mcp-example-btn:hover {
    background-color: var(--ska-info-light);
}

.mcp-cursor-example {
    background-color: var(--ska-purple);
}

.mcp-cursor-example:hover {
    background-color: var(--ska-purple-light);
}

.mcp-tester-body pre {
    background-color: var(--ska-surface-alt);
    padding: 10px;
    border-radius: 4px;
    overflow-x: auto;
    color: var(--ska-text);
}

.mcp-header-info {
    text-align: center;
    margin-bottom: 20px;
    color: var(--ska-text-muted);
}

/* ═══════════════════════════════════════════════════
   UTILITY CLASSES
   ═══════════════════════════════════════════════════ */

/* Color dot — replaces inline style="background:{{ color }}" */
.ska-color-dot {
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    vertical-align: middle;
    margin-right: 6px;
    border: 2px solid var(--ska-border-medium);
    background: attr(data-color);
}

.ska-color-dot--lg {
    width: 16px;
    height: 16px;
    margin-right: 8px;
}

.ska-text-right {
    text-align: right;
}

.ska-text-left {
    text-align: left;
}

.ska-text-center {
    text-align: center;
}

.ska-text-bold {
    font-weight: 500;
}

.ska-text-semibold {
    font-weight: 600;
}

.ska-overflow-auto {
    overflow-x: auto;
}

.ska-mb-sm {
    margin-bottom: 8px;
}

.ska-mb-md {
    margin-bottom: 16px;
}

.ska-mb-lg {
    margin-bottom: 24px;
}

.ska-mt-sm {
    margin-top: 8px;
}

.ska-mt-md {
    margin-top: 16px;
}

/* ═══════════════════════════════════════════════════
   KPI STAT CARDS (config-inspection, memory-dashboard)
   Accent-colored number values on neutral surface cards.
   All colors derive from ska-tokens — zero hardcode.
   ═══════════════════════════════════════════════════ */

/* -- Grid -- */
.ska-kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 16px;
    margin-bottom: 24px;
}

/* -- Card -- */
.ska-kpi-card {
    background: var(--ska-surface);
    border: 1px solid var(--ska-border-medium);
    border-radius: var(--ska-radius-md);
    padding: 16px;
    text-align: center;
    box-shadow: var(--ska-shadow-sm);
    transition: box-shadow 0.2s;
}

.ska-kpi-card:hover {
    box-shadow: var(--ska-shadow-md);
}

.ska-kpi-card .label {
    margin: 0 0 4px;
    font-size: 11px;
    color: var(--ska-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 500;
}

.ska-kpi-card .val {
    font-size: 28px;
    font-weight: 700;
}

.ska-kpi-card .sub {
    font-size: 11px;
    color: var(--ska-text-subtle);
    margin-top: 4px;
}

/* -- Accent colors for .val numbers -- */
.ska-accent-blue {
    color: var(--ska-info);
}

.ska-accent-green {
    color: var(--ska-success);
}

.ska-accent-amber {
    color: var(--ska-warning);
}

.ska-accent-red {
    color: var(--ska-danger);
}

.ska-accent-purple {
    color: var(--ska-purple);
}

.ska-accent-cyan {
    color: var(--ska-primary);
}

/* -- Sections (h3 with bottom border) -- */
.ska-kpi-section {
    margin-bottom: 24px;
}

.ska-kpi-section h3 {
    font-size: 15px;
    margin: 0 0 8px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--ska-border-medium);
    color: var(--ska-text);
}

/* -- Metadata grid (dt/dd) -- */
.ska-kpi-meta {
    display: grid;
    grid-template-columns: 180px 1fr;
    gap: 4px 12px;
    font-size: 13px;
    margin-bottom: 16px;
}

.ska-kpi-meta dt {
    font-weight: 600;
    color: var(--ska-text-muted);
}

.ska-kpi-meta dd {
    margin: 0;
    font-family: var(--ska-font-mono);
    font-size: 12px;
    word-break: break-all;
    color: var(--ska-text);
}

/* -- Badge (monospace tag) -- */
.ska-kpi-badge {
    display: inline-block;
    background: var(--ska-surface-alt);
    border: 1px solid var(--ska-border-medium);
    border-radius: 4px;
    padding: 2px 8px;
    font-family: var(--ska-font-mono);
    font-size: 11px;
    margin: 2px;
    color: var(--ska-text);
}

/* -- Quiet/muted text -- */
.ska-text-quiet {
    color: var(--ska-text-subtle);
}

/* -- Data table variant (compact, for breakdown/dist tables) -- */
.ska-kpi-table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 16px;
}

.ska-kpi-table th,
.ska-kpi-table td {
    padding: 8px 12px;
    text-align: left;
    border-bottom: 1px solid var(--ska-border);
    font-size: 13px;
}

.ska-kpi-table th {
    font-weight: 600;
    background: var(--ska-surface-alt);
    color: var(--ska-text-muted);
}

.ska-kpi-table .num {
    text-align: right;
    font-family: var(--ska-font-mono);
}

/* -- Collapsible sections -- */
.ska-kpi-toggle {
    cursor: pointer;
    user-select: none;
}

.ska-kpi-toggle::before {
    content: "▶ ";
    font-size: 10px;
}

.ska-kpi-toggle.open::before {
    content: "▼ ";
}

.ska-hidden {
    display: none;
}

/* -- Code/instruction block -- */
.ska-kpi-code {
    background: var(--ska-surface-alt);
    border: 1px solid var(--ska-border-medium);
    border-radius: var(--ska-radius-sm);
    padding: 16px;
    font-family: var(--ska-font-mono);
    font-size: 12px;
    line-height: 1.5;
    white-space: pre-wrap;
    word-wrap: break-word;
    max-height: 500px;
    overflow-y: auto;
    color: var(--ska-text);
}

/* -- Score pills (memory dashboard relevance) -- */
.ska-score-pill {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 11px;
    font-weight: 600;
}

.ska-score-pill.green {
    background: var(--ska-badge-green-bg);
    color: var(--ska-badge-green-text);
}

.ska-score-pill.amber {
    background: var(--ska-badge-yellow-bg);
    color: var(--ska-badge-yellow-text);
}

.ska-score-pill.red {
    background: var(--ska-badge-red-bg);
    color: var(--ska-badge-red-text);
}

/* -- Preference items -- */
.ska-pref-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 12px;
    background: var(--ska-surface-alt);
    border-radius: 8px;
    font-size: 13px;
    border: 1px solid var(--ska-border);
    color: var(--ska-text);
}

.ska-pref-badge {
    font-size: 10px;
    padding: 1px 6px;
    border-radius: 8px;
    background: var(--ska-surface);
    border: 1px solid var(--ska-border-medium);
    color: var(--ska-text-muted);
    flex-shrink: 0;
}

/* -- Alert/error box -- */
.ska-alert-error {
    background: var(--ska-danger-bg);
    border: 1px solid var(--ska-danger);
    padding: 16px;
    border-radius: 8px;
    margin-bottom: 16px;
    color: var(--ska-danger);
}

/* Hermana informativa de `.ska-alert-error`. Existía el rojo y no el azul, así que la pantalla
   de Integraciones —que la usa para explicar cada bloque— pintaba esos avisos sin recuadro.
   Medido el 6-sep-2026. */
.ska-alert-info {
    background: var(--ska-info-bg);
    border: 1px solid var(--ska-info);
    padding: 16px;
    border-radius: 8px;
    margin-bottom: 16px;
    color: var(--ska-text-on-info);
}

/* Rótulo corto de estado al lado de un título (el nombre de la organización, «sin conectar»). */
.ska-admin-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 10px;
    border: 1px solid var(--ska-border);
    border-radius: 999px;
    background: var(--ska-surface-alt);
    color: var(--ska-text-secondary);
    font-size: 12px;
    line-height: 20px;
    white-space: nowrap;
}

/* Texto de ayuda debajo de un campo o de un bloque. */
.ska-admin-hint {
    color: var(--ska-text-muted);
    font-size: 12px;
    line-height: 18px;
    margin-top: 4px;
}

/* ═══════════════════════════════════════════════════
   KNOWLEDGE GRAPH (memory-dashboard — Cytoscape.js)
   ═══════════════════════════════════════════════════ */

/* Wrapper: provides positioning context for controls + legend */
.md-graph-wrap {
    position: relative;
}

/* Canvas container where Cytoscape renders */
.md-graph-canvas {
    width: 100%;
    height: 900px;
    border-radius: var(--ska-radius-md);
    border: 1px solid var(--ska-border);
    background: var(--ska-surface);
    position: relative;
}

/* Zoom / fit controls (top-right) */
.md-graph-controls {
    position: absolute;
    top: 24px;
    right: 24px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    z-index: 10;
}

.md-graph-controls button {
    width: 32px;
    height: 32px;
    border-radius: 8px;
    border: 1px solid var(--ska-border-medium);
    background: var(--ska-surface-alt);
    color: var(--ska-text);
    font-size: 16px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.15s;
}

.md-graph-controls button:hover {
    background: var(--ska-border-medium);
}

.md-graph-controls button.fit {
    font-size: 12px;
}

/* Tooltip (follows mouse on node hover) */
.md-graph-tooltip {
    position: absolute;
    display: none;
    padding: 8px 14px;
    border-radius: 8px;
    background: var(--ska-surface-alt);
    border: 1px solid var(--ska-border-medium);
    color: var(--ska-text);
    font-size: 11px;
    line-height: 1.5;
    max-width: 260px;
    z-index: 999;
    pointer-events: none;
    box-shadow: var(--ska-shadow-md);
}

/* Floating legend card (bottom-left inside graph) */
.md-graph-legend {
    position: absolute;
    bottom: 16px;
    left: 16px;
    z-index: 10;
    background: var(--ska-surface-alt);
    border: 1px solid var(--ska-border-medium);
    border-radius: 10px;
    padding: 10px 14px;
    box-shadow: var(--ska-shadow-sm);
    font-size: 10px;
    color: var(--ska-text-subtle);
    line-height: 1.8;
    max-width: 200px;
}

.md-graph-legend-title {
    font-weight: 700;
    font-size: 11px;
    margin-bottom: 4px;
    color: var(--ska-text);
}

/* Node-type swatch (colored dot) */
.md-graph-swatch {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    vertical-align: middle;
    margin-right: 4px;
}

.md-graph-swatch.rect {
    border-radius: 4px;
}

.md-graph-swatch.user {
    background: var(--ska-graph-user);
}

.md-graph-swatch.person {
    background: var(--ska-graph-person);
}

.md-graph-swatch.organization {
    background: var(--ska-graph-org);
}

.md-graph-swatch.place {
    background: var(--ska-graph-place);
}

/* Edge-type swatch (colored line) */
.md-graph-edge-swatch {
    display: inline-block;
    width: 16px;
    vertical-align: middle;
    margin-right: 4px;
}

.md-graph-edge-swatch.relation {
    border-top: 2px solid var(--ska-graph-edge-relation);
}

.md-graph-edge-swatch.agent {
    border-top: 2px dashed var(--ska-graph-edge-agent);
}

.md-graph-edge-swatch.crossref {
    border-top: 1px dotted var(--ska-graph-edge-crossref);
}

.md-graph-edge-swatch.entity-rel {
    border-top: 2px dashed var(--ska-graph-edge-entity);
}

/* Divider inside legend */
.md-graph-legend-divider {
    margin-top: 4px;
    border-top: 1px solid var(--ska-border);
    padding-top: 4px;
}

/* No-data empty state */
.md-graph-empty {
    padding: 40px;
    text-align: center;
    color: var(--ska-text-subtle);
    font-size: 14px;
}

/* ═══════════════════════════════════════════════════
   CONFIG INSPECTION (config-inspection view)
   Copy buttons, collapsible sections, context groups.
   All colors derive from ska-tokens — zero hardcode.
   ═══════════════════════════════════════════════════ */

/* -- Copy button -- */
.ci-copy-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 10px;
    border: 1px solid var(--ska-border-medium);
    border-radius: 6px;
    background: transparent;
    color: var(--ska-text-muted);
    font-size: 11px;
    cursor: pointer;
    transition: all 0.15s ease;
    margin-left: 8px;
    vertical-align: middle;
}

.ci-copy-btn:hover {
    background: var(--ska-surface-alt);
    color: var(--ska-text);
    border-color: var(--ska-border-medium);
}

.ci-copy-btn.copied {
    color: var(--ska-success);
    border-color: var(--ska-success);
}

/* -- Collapsible toggle (replaces ska-kpi-toggle for smooth animation) -- */
.ci-toggle {
    cursor: pointer;
    user-select: none;
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 12px 0 4px;
    padding: 8px 12px;
    border-radius: var(--ska-radius-sm);
    transition: background 0.15s ease;
    font-size: 14px;
    font-weight: 600;
    color: var(--ska-text);
}

.ci-toggle:hover {
    background: var(--ska-surface-alt);
}

.ci-toggle::before {
    content: '▶';
    font-size: 10px;
    transition: transform 0.2s ease;
    color: var(--ska-text-muted);
    flex-shrink: 0;
}

.ci-toggle.open::before {
    transform: rotate(90deg);
}

/* -- Collapsible content (smooth expand/collapse) -- */
.ci-collapsible {
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s ease;
}

.ci-collapsible.open {
    max-height: 20000px;
}

/* -- Dynamic context groups (user info, security, time, etc.) -- */
.ci-ctx-group {
    margin: 6px 0;
    padding: 8px 12px;
    border-left: 3px solid var(--ska-border-medium);
    border-radius: 0 var(--ska-radius-sm) var(--ska-radius-sm) 0;
    background: var(--ska-surface-alt);
}

.ci-ctx-group-label {
    font-size: 12px;
    font-weight: 600;
    margin-bottom: 4px;
    color: var(--ska-text);
}

.ci-ctx-line {
    font-family: var(--ska-font-mono);
    font-size: 12px;
    line-height: 1.5;
    color: var(--ska-text-muted);
    word-break: break-word;
}

/* -- Disclaimer (admin preview warning) -- */
.ci-disclaimer {
    margin: 20px 0 0;
    padding: 10px 16px;
    border-radius: var(--ska-radius-sm);
    background: var(--ska-warning-bg);
    border: 1px solid var(--ska-warning);
    font-size: 12px;
    color: var(--ska-text-muted);
}

/* -- Role display -- */
.ci-role-line {
    margin: 12px 0 0;
    font-size: 13px;
}

/* -- Context lines heading -- */
.ci-ctx-heading {
    font-size: 13px;
    margin: 0 0 8px;
    color: var(--ska-text-muted);
}

/* ═══════════════════════════════════════════════════
   DRIVE SYNC FILTER DASHBOARD
   ═══════════════════════════════════════════════════ */

/* ── Stats grid ── */
.dsf-stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 12px;
    margin-bottom: 16px;
}

/* ── Stat value color accents ── */
.dsf-stat-pending {
    color: var(--ska-warning);
}

.dsf-stat-processing {
    color: var(--ska-info);
}

.dsf-stat-completed {
    color: var(--ska-success);
}

.dsf-stat-failed {
    color: var(--ska-danger);
}

/* ── Section header with inline action ── */
.dsf-section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 2px solid var(--ska-primary);
    margin-bottom: 24px;
    padding-bottom: 12px;
}

.dsf-section-header .gcm-section-title {
    margin: 0;
    padding: 0;
    border-bottom: none;
    flex: 1;
}

/* ── Form card ── */
.dsf-form-card {
    border-left: 3px solid var(--ska-primary);
    margin-bottom: 16px;
}

.dsf-form-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--ska-text);
    margin-bottom: 16px;
}

.dsf-form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 12px;
    margin-bottom: 12px;
}

.dsf-form-group {
    margin-bottom: 8px;
}

.dsf-label {
    display: block;
    font-size: 12px;
    font-weight: 500;
    color: var(--ska-text-muted);
    margin-bottom: 4px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.dsf-input {
    width: 100%;
    padding: 8px 12px;
    font-size: 13px;
    border: 1px solid var(--ska-border-medium);
    border-radius: var(--ska-radius);
    background: var(--ska-surface-alt);
    color: var(--ska-text);
    transition: border-color 0.15s ease;
    box-sizing: border-box;
}

.dsf-input:focus {
    outline: none;
    border-color: var(--ska-accent);
    box-shadow: 0 0 0 2px var(--ska-primary-glow);
}

.dsf-form-checkbox {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-top: 20px;
}

.dsf-form-checkbox .dsf-label {
    display: inline;
    text-transform: none;
    margin-bottom: 0;
    cursor: pointer;
}

.dsf-form-actions {
    display: flex;
    gap: 8px;
    margin-top: 16px;
    padding-top: 12px;
    border-top: 1px solid var(--ska-border);
}

/* ── Filter cards ── */
.dsf-filter-card {
    transition: opacity 0.15s ease;
}

.dsf-filter-inactive {
    opacity: 0.6;
}

.dsf-filter-inactive:hover {
    opacity: 0.85;
}

.dsf-filter-details {
    margin-bottom: 12px;
}

.dsf-filter-details code {
    font-family: var(--ska-font-mono);
    font-size: 12px;
    background: var(--ska-surface-alt);
    padding: 2px 6px;
    border-radius: 3px;
    color: var(--ska-text-secondary);
}

/* ── Stat chips inside filter cards ── */
.dsf-filter-stats {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 8px;
    padding-top: 12px;
    border-top: 1px solid var(--ska-border);
}

.dsf-stat-chip {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 3px 10px;
    border-radius: 12px;
    font-size: 12px;
    font-weight: 600;
}

.dsf-chip-pending {
    background: var(--ska-warning-bg);
    color: var(--ska-warning);
}

.dsf-chip-processing {
    background: var(--ska-info-bg);
    color: var(--ska-info);
}

.dsf-chip-completed {
    background: var(--ska-success-bg);
    color: var(--ska-success);
}

.dsf-chip-failed {
    background: var(--ska-danger-bg);
    color: var(--ska-danger);
}

.dsf-chip-total {
    background: var(--ska-surface-alt);
    color: var(--ska-text-muted);
}

/* ── Visibility toggle ── */
.dsf-hidden {
    display: none !important;
}

/* Utility: accent-colored link */
.ska-link-accent {
    color: var(--ska-accent);
    text-decoration: none;
}

.ska-link-accent:hover {
    text-decoration: underline;
    opacity: 0.85;
}

/* ═══════════════════════════════════════════════════
   AGENT CONTEXT SELECTOR
   Estilos de la barra de selector que usa `brand_selector.html` y
   cualquier dashboard que necesite mostrar "estás operando como X".
   ═══════════════════════════════════════════════════ */

.ska-selector-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 16px;
    padding: 16px 20px;
    margin-bottom: 24px;
    background: var(--ska-surface);
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-md);
}

.ska-selector-bar--single {
    justify-content: flex-start;
}

.ska-selector-info {
    display: flex;
    align-items: center;
    gap: 12px;
}

.ska-selector-avatar {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    object-fit: cover;
    border: 1px solid var(--ska-border);
}

.ska-selector-avatar-placeholder {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    background: color-mix(in srgb, var(--ska-primary) 10%, var(--ska-bg));
    color: var(--ska-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    border: 1px solid var(--ska-border);
}

.ska-selector-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ska-text-muted);
    font-weight: 600;
    margin-bottom: 2px;
}

.ska-selector-name {
    font-weight: 600;
    color: var(--ska-text);
    display: flex;
    align-items: center;
    gap: 8px;
}

.ska-selector-type-badge {
    font-size: 10px;
}

.ska-selector-form {
    flex-shrink: 0;
    min-width: 200px;
}

.ska-selector-form form {
    margin: 0;
}

.ska-selector-select {
    width: 100%;
    padding: 8px 12px;
    border-radius: 8px;
    border: 1px solid var(--ska-border);
    background: var(--ska-surface);
    color: var(--ska-text);
    font-size: 14px;
    font-weight: 500;
    outline: none;
    appearance: auto;
}

.ska-selector-select:focus {
    border-color: var(--ska-primary);
    box-shadow: 0 0 0 2px var(--ska-primary-glow);
}

/* ── UI Element: Code Pills (Code Blocks / JSON representation) ── */
.ska-code-pill {
    display: inline-block;
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
    font-size: 0.75rem;
    /* text-xs */
    padding: 0.25rem 0.5rem;
    /* px-2 py-1 */
    background-color: var(--ska-surface-alt);
    border: 1px solid var(--ska-border);
    border-radius: 0.375rem;
    /* rounded-md */
    color: var(--ska-text-secondary);
}

/* ═══════════════════════════════════════════════════════════════════════
 * Changelist con CARDS — neutralizar la "caja" del toolbar de Unfold.
 *
 * El toolbar de búsqueda de Unfold (el div que contiene #toolbar) está
 * estilizado como el ENCABEZADO de una caja que la TABLA de resultados por
 * defecto completa: en desktop trae `lg:border` + `lg:rounded-t-default`
 * (borde 4 lados, solo esquinas de arriba redondeadas) + `lg:pb-11` (44px de
 * padding inferior) + `lg:-mb-8` (margin inferior -32px, que jala hacia arriba
 * lo que sigue para encajar la tabla).
 *
 * Pero TODOS nuestros changelists renderizan CARDS (no la tabla), así que la
 * caja nunca se cierra → queda: (1) padding muerto bajo "Type to search",
 * (2) una línea suelta (el borde inferior del toolbar), (3) los cards jalados
 * a traslaparse con esa línea (`-mb-8`).
 *
 * Fix: en card-pages el toolbar vuelve a ser una fila simple (buscador +
 * Filtros) y los cards flotan debajo (depth-over-borders del DS — los cards ya
 * traen su sombra).
 *
 * SCOPE SEGURO: SOLO changelists SIN la tabla default (`table#result_list`).
 * Las páginas de tabla (clinicalentitymention, consultorioprofile,
 * funneltransition, *suggestion, llm/rekognition usage, etc.) SÍ tienen
 * `table#result_list` → quedan EXCLUIDAS y conservan su caja intacta. Solo
 * aplica en `lg` (donde Unfold pinta la caja). No toca buscador ni filtros
 * (son hijos). Ver memoria [[spa-nav-htmx-boost]] / [[cards-changelist-keep-bulk-actions]].
 *
 * ⚠️ ENTREGA (causa de "siempre ocurre"): este archivo es SHARED y se carga sin
 * hash de manifest. Si su `?v=` no se bumpea, Cloudflare/el browser sirven una
 * copia VIEJA y este fix no llega → la caja vuelve a verse con los cards encima.
 * Fuente de verdad del `?v=`: `core/settings/unfold.py` STYLES (ska-admin-components.css?v=N).
 * Al editar este archivo, BUMPEAR ese `?v=N` (y los `<link>` directos que lo
 * dupliquen). Ver memoria [[changelist-toolbar-box-overlap]] + ADMIN_PATTERN §2.5.A.
 *
 * El contenedor de cards puede llamarse `*-cards` (molde per-prefijo) o
 * `ska-card-grid` (vocabulario compartido de inventory.css); ambos cubiertos.
 * ═══════════════════════════════════════════════════════════════════════ */
/* BORDE + redondeo + sombra de la caja: FUERA solo en CARD-changelists.
 *
 * ⚠️ Guard `:not(:has(#result_list))`: la TABLA default de Unfold renderiza
 * `<table id="result_list">`; un card-changelist (override de result_list) NO.
 * Así las TABLAS reales (petownership, catálogos) CONSERVAN su caja (borde) y solo
 * las cards la pierden + ganan espacio arriba. Ver ADR-019 (revierte ADR-006).
 *   Trade-off: `:has()` lo ignora Firefox <121 → ahí la regla entera se cae →
 * TODA changelist conserva la caja (cards muestran el borde; degradación cosmética
 * aceptable para un admin interno 2026; nada se rompe). Se eligió sobre marcar ~31
 * templates a mano (footgun: cada card-changelist nueva tendría que recordar la
 * marca o la caja vuelve). El combo `[class*="lg:pb-11"][class*="lg:-mb-8"]`
 * identifica el wrapper del toolbar. */
body.change-list:not(:has(#result_list)) [class*="lg:pb-11"][class*="lg:-mb-8"] {
    border: 0 none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
}

@media (min-width: 1024px) {

    /* pb-11 (padding muerto) + -mb-8 (margen negativo) solo existen en lg.
     * margin-top: separa el toolbar del header (simétrico al bottom, alineado con
     * el buscador del sidebar) — antes quedaba pegado arriba en card-changelists. */
    body.change-list:not(:has(#result_list)) [class*="lg:pb-11"][class*="lg:-mb-8"] {
        padding: 0 !important;
        margin-top: 1rem !important;
        margin-bottom: 1rem !important;
    }

    /* Contenedor de cards: ya no completa una caja → flota sin borde.
     *
     * ── EL AIRE DE ARRIBA ES UN DEFAULT, NO UNA IMPOSICIÓN ──────────────────────────────
     * Hasta el 05-sep-2026 acá decía `margin-top: 0 !important; padding-top: 4px
     * !important`, y ese `!important` con especificidad (2,2,1) le ganaba a CUALQUIER cosa
     * que un producto escribiera para su propia lista. El costo está medido y es de los
     * caros: Taller Kids pidió el 03-sep despegar la barra de «seleccionar todo» de la
     * primera tarjeta, el arreglo (`29f9d416`) subió `.tk-cards { margin-top }` de 12px a
     * 16px, y en pantalla NO CAMBIÓ NADA — porque en ≥1024px el margen de esa lista lo
     * decidía esta regla y valía 0. Sergio lo volvió a pedir el 05-sep, en mayúsculas.
     *
     * Y el diagnóstico costó dos días de leer el CSS del producto, que decía la verdad: el
     * archivo que mentía era éste, y no aparece en ningún `grep` de `.tk-cards` porque caza
     * por SUBCADENA. Ése es el modo de falla que las variables cierran: el producto declara
     * su aire y lo que ve en su archivo es lo que la pantalla hace.
     *
     * Los DEFAULT son exactamente los valores que había, así que ninguna de las 23
     * changelists con `[class*="cards"]` —17 de javiermartinez, 4 de taller-kids, 2 de
     * prismo_so— se mueve un píxel mientras no declare las variables. Eso lo mide
     * `test_la_puerta_del_aire_de_las_cards.py`.
     *
     * ⚠️ El `!important` SE QUEDA, y es a propósito: sigue haciendo falta para neutralizar
     * las utilidades de Unfold que ponen el margen de la caja. Lo que cambia es QUIÉN
     * decide el valor. Una custom property se sobreescribe con especificidad normal —el
     * `!important` está en el CONSUMO del valor, no en la property—, así que el producto no
     * necesita pelear ni escribir `!important` para ser oído.
     *
     * ── EL `:not()` NO ES DEFENSIVO: SACA UN ELEMENTO QUE ESTA REGLA NUNCA QUISO ────────────
     * `[class*="cards"]` caza por SUBCADENA, así que además de los contenedores agarraba
     * `tk-cards__bar` —la barra de «Marcar todos los cobros de esta página» de Taller Kids, que
     * es hija directa del mismo form—. No es un contenedor de cards: es la cabecera de la lista.
     * Y como esta regla llega con `!important`, le estaba imponiendo `margin-top: 0` sobre el
     * `12px` que su propio CSS declara, más un `padding-top: 4px` que nadie le pidió. Medido en
     * Chrome el 05-sep-2026 sobre el HTML que sirve Django: la barra rendía `margin-top: 0px` y
     * `padding: 4px 18px 0px` contra los `margin-top: 12px` y `padding: 0 18px` de su archivo.
     *
     * El doble guión bajo es la marca BEM del HIJO de un bloque, y un hijo nunca es el
     * contenedor que esta regla describe. Barrido de los tres productos el 05-sep-2026: de los
     * 29 elementos con `cards` en la clase dentro de una changelist, 23 son contenedores y 6
     * llevan `cards__` — los seis de Taller Kids, y de ésos **sólo `tk-cards__bar` es hijo
     * directo del form**; `__toggle` y `__counter` viven dentro de la barra, así que el `>` ya
     * no los alcanzaba. O sea que este `:not()` cambia exactamente DOS pantallas, y ninguna de
     * las 23 listas.
     *
     * Va también en `card-grid` aunque hoy no exista un solo `card-grid__`: son la misma regla
     * y la misma intención, y dejar uno con la protección y el otro sin ella es la asimetría
     * que nadie recuerda el día que aparece el caso. */
    body.change-list:not(:has(#result_list)) #changelist-form>[class*="cards"]:not([class*="cards__"]),
    body.change-list:not(:has(#result_list)) #changelist-form>[class*="card-grid"]:not([class*="card-grid__"]) {
        border: 0 !important;
        border-radius: 0 !important;
        margin-top: var(--ska-cards-mt, 0) !important;
        padding-top: var(--ska-cards-pt, 4px) !important;
    }

    /* Estado vacío: que no quede empujado dentro de la (ex)caja. */
    body.change-list:not(:has(#result_list)) #changelist-form>[class*="empty"] {
        margin-top: 0 !important;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
 * Sidebar — ítem ACTIVO (fuente ÚNICA de verdad) — "estás aquí".
 *
 * El estilo del ítem activo del menú vive SOLO acá. El HTML (app_list.html) y el
 * JS de navegación AJAX (spa-nav.js) únicamente togglean la clase marcadora
 * `.active` — NINGUNO lleva clases de color. Antes el estilo activo estaba
 * duplicado como utilities Tailwind en los dos lados; al navegar con hx-boost el
 * sidebar NO se re-renderiza y spa-nav.js repintaba el activo con una copia
 * desactualizada de esas clases → el seleccionado "desaparecía" (texto del color
 * de marca sobre fondo oscuro). Centralizar mata esa desincronización.
 *
 * Tratamiento (3 señales, inconfundible) — antes el activo usaba el MISMO
 * `bg-base-100`/`bg-base-800` que el hover → activo y hover se veían idénticos
 * (no se sabía en qué página estabas):
 *   1) BARRA de acento a la izquierda (box-shadow inset, no desplaza el flex y
 *      respeta `rounded-default`). Color de MARCA (`--color-primary-600`, igual
 *      que #spa-progress): decorativo, siempre visible en cualquier marca/tema.
 *      Se hereda al modo dark (la regla `.dark` no lo pisa).
 *   2) FONDO un paso más fuerte que el hover (base-200 vs hover base-100;
 *      dark base-700 vs hover base-800) → activo ≠ hover.
 *   3) Texto `font-important` + 600. Marca-agnóstico: el TEXTO usa font tokens
 *      (no `primary`), legible con cualquier marca (incl. Pet Club carbón).
 * La specificity con #id gana al hover utility → el activo conserva fondo+barra
 * aun en hover. Al editar este archivo BUMPEAR el `?v=` en core/settings/unfold.py
 * STYLES. Ver [[admin-sidebar-brand-agnostic-and-unfold-purgecss]].
 * ═══════════════════════════════════════════════════════════════════════ */
#nav-sidebar-apps a.active {
    /* Más sutil + armonizado al tono de la sección: tinte de MARCA translúcido (mismo
       hue que el fondo del escenario) en vez del gris opaco base-200. La barra de
       acento inset + el peso 600 + el color font-important siguen siendo la señal
       fuerte de "estás aquí"; el fondo solo es un escalón suave sobre el del grupo. */
    background-color: color-mix(in srgb, var(--color-primary-600) 13%, transparent);
    font-weight: 600;
    color: var(--color-font-important-light);
    box-shadow: inset 3px 0 0 var(--color-primary-600);
}

.dark #nav-sidebar-apps a.active {
    background-color: color-mix(in srgb, var(--color-primary-600) 18%, transparent);
    color: var(--color-font-important-dark);
}

/* ═══════════════════════════════════════════════════════════════════════
 * Sidebar — TÍTULO de sección (CONTABILIDAD, etc.) — diferenciarlo del ítem.
 *
 * Antes el título era un LABEL muteado (`text-font-subtle-*`, inline en
 * app_list.html) → se leía casi igual que sus ítems y se perdía ("¿esto es
 * encabezado o ítem?"). Bajar el tono lo empeoró. La jerarquía la dan ahora
 * tipografía (uppercase + text-xs) + TRACKING + un DIVISOR por sección, NO un
 * color apagado: el texto vuelve a contraste legible (`font-default`, el mismo
 * del cuerpo). #id gana a la utility `.text-font-subtle-*` inline sin !important.
 * Dentro de #nav-sidebar-apps los únicos <h2> son los títulos de sección (el
 * bloque "All applications" queda FUERA de ese id). Iconos teal intactos (su
 * `.text-teal-500` propio gana por su clase). BUMPEAR `?v=` al editar. */
#nav-sidebar-apps h2 {
    color: var(--color-font-default-light);
    font-size: 0.6875rem;
    /* 11px — menor que el ítem (14px): se lee como LABEL, no como fila */
    letter-spacing: 0.06em;
    border-top: 1px solid var(--color-base-200);
    margin-top: 0.85rem;
    /* más aire antes de la sección (whitespace = corte, consenso UX) */
    padding-top: 0.95rem;
}

.dark #nav-sidebar-apps h2 {
    color: var(--color-font-default-dark);
    border-top-color: var(--color-base-800);
}

/* Ítems ANIDADOS bajo su título (patrón árbol VS Code/Notion): indent + guía
 * vertical → los ítems CUELGAN del título como su contenido, no como hermanos.
 * Esta era la señal que faltaba: sin anidación, "Catálogo Clínico" se leía igual
 * que "Especie/Razas/Condiciones" (mezclados). Selector DESCENDIENTE por SimpleBar;
 * el <ol> cerrado va display:none → la guía solo aparece en secciones ABIERTAS
 * (refuerza el cue de abierto). NO tocar el <a> (su `-mx-3 px-3` estira el fondo de
 * hover/activo a todo el ancho — moverlo rompería esos estados). */
#nav-sidebar-apps ol {
    padding-left: 2.35rem;
    margin-left: 1rem;
    border-left: 1px solid var(--color-base-200);
}

.dark #nav-sidebar-apps ol {
    border-left-color: var(--color-base-800);
}

/* Sin divisor donde sobraría: el PRIMER grupo (pegado al buscador) y los grupos
 * que YA traen un divisor arriba — la banda `.ska-admin-band-label`
 * ("Administración") o un `<hr>` separador — para no dibujar línea doble. */
#nav-sidebar-apps>div:first-child h2,
#nav-sidebar-apps .ska-admin-band-label~h2,
#nav-sidebar-apps hr~h2 {
    border-top: 0;
    margin-top: 0;
    padding-top: 0.375rem;
}

/* ═══════════════════════════════════════════════════════════════════════
 * Sidebar — INTERIOR de una sección desplegada (agrupa sus ítems).
 *
 * #nav-sidebar-apps lleva `data-simplebar`: SimpleBar REESTRUCTURA el DOM en
 * runtime, envolviendo el contenido en .simplebar-wrapper/.../.simplebar-content.
 * Por eso el grupo `div[x-data]` NO es hijo directo de #nav-sidebar-apps en el
 * DOM vivo (sí lo es en el HTML del servidor) → combinador DESCENDIENTE, no `>`.
 * Verificado en el DOM vivo: `#nav-sidebar-apps div[x-data] ol` = 11 grupos;
 * con `>` desde #nav-sidebar-apps = 0 (ese era el bug). El <ol> sí es hijo
 * directo del div[x-data].
 *
 * Tratamiento (referencias: Tailwind UI / Linear / Notion / Vercel / Stripe /
 * Unfold default + DESIGN_SYSTEM propio). Consenso: la señal fuerte es el ÍTEM
 * ACTIVO (fondo base-200/700 + barra inset primary — intacto). "Abierto" se
 * comunica por el CHEVRON rotado; el tinte es solo refuerzo, ultra sutil, plano,
 * sin rail ni radio. ANTIPATRÓN evitado: rellenar TODOS los títulos o el bloque.
 *
 * LLAVE = la sección que CONTIENE el ítem ACTIVO (`:has(a.active)`), NO cualquier
 * sección abierta. El sidebar usa hx-boost y NO se re-renderiza al navegar → el
 * estado abierto/cerrado PERSISTE en el cliente (Alpine); keyear en "abierto"
 * dejaba iluminada la sección ANTERIOR al cambiar de vista. `a.active` lo actualiza
 * spa-nav.js en cada navegación → el realce sigue a la página y no queda obsoleto.
 *   (a) HEADER de la sección activa → primary ~5% (light)/9% (dark). Solo ese <h2>.
 *   (b) INTERIOR (<ol>) de la sección activa → primary ~3% (light)/6% (dark), un
 *       escalón por debajo y muy por debajo del ítem activo. Marca-agnóstico
 *       (primary carbón de Pet Club ⇒ casi nulo; jerarquía por chevron + indent).
 * BUMPEAR `?v=` en unfold.py al editar. */
/* La sección ACTIVA = UN contenedor: un solo fondo en el GRUPO (no dos fondos
 * separados en título y contenido). Antes el fondo del contenido iba INDENTADO por
 * el árbol → no cuadraba con el del título ("descuadrado"). Con una sola capa detrás
 * de todo el grupo, título y contenido quedan ALINEADOS; los ítems siguen indentados
 * DENTRO. El título pierde divisor + margen (el contenedor ya lo separa). */
#nav-sidebar-apps div[x-data]:has(a.active) {
    background-color: color-mix(in srgb, var(--color-primary-600) 5%, transparent);
    padding-top: 0.1rem;
    padding-bottom: 0.4rem;
}

.dark #nav-sidebar-apps div[x-data]:has(a.active) {
    background-color: color-mix(in srgb, var(--color-primary-600) 9%, transparent);
}

#nav-sidebar-apps div[x-data]:has(a.active)>h2 {
    background-color: transparent;
    border-top-color: transparent;
    margin-top: 0;
    padding-top: 0.55rem;
}

#nav-sidebar-apps div[x-data]:has(a.active)>ol {
    background-color: transparent;
    padding-top: 0.2rem;
    padding-bottom: 0.2rem;
}

/* ── Barra de progreso de navegación (spa-nav.js / hx-boost) ─────────────────
   Feedback de "cargando la página siguiente": una barra fina fija arriba que
   arranca en htmx:beforeRequest y termina en htmx:afterRequest. Sin esto, un
   click boosted no daba ninguna señal ("no pasa nada"). Color = primary de la
   marca activa (Unfold expone --color-primary-600 como rgb(...) por tenant). */
#spa-progress {
    position: fixed;
    top: 0;
    left: 0;
    height: 3px;
    width: 0;
    background: var(--color-primary-600, #2563eb);
    box-shadow: 0 0 8px var(--color-primary-600, #2563eb);
    z-index: 99999;
    opacity: 0;
    pointer-events: none;
    border-top-right-radius: 3px;
    border-bottom-right-radius: 3px;
}

#spa-progress.spa-progress--active {
    opacity: 1;
    width: 88%;
    /* Avance perceptual: rápido al inicio, asintótico (nunca llega al 100% solo). */
    transition: width 9s cubic-bezier(0.1, 0.7, 0.2, 1), opacity 0.15s ease;
}

#spa-progress.spa-progress--done {
    opacity: 0;
    width: 100%;
    transition: width 0.2s ease, opacity 0.4s ease 0.2s;
}

@media (prefers-reduced-motion: reduce) {
    #spa-progress.spa-progress--active {
        transition: opacity 0.15s ease;
        width: 88%;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
 * Input/panel primitives — vocabulario ÚNICO de inputs SUELTOS del admin
 * (filtros de listas/reports, POS, forms de acción en detalles). Reemplaza el
 * CSS de inputs legacy del carril §1 (account_balance), ya erradicado (ADR-020).
 * Mismo look que los .jmf-field (forms) → un solo lenguaje visual. NO confundir
 * con .jmf-* (esos viven en ska_admin_form.css, scope .jmf-wrap, para el form).
 * Solo tokens var(--ska-*). Ver docs/admin/surfaces/page.md.
 * ═══════════════════════════════════════════════════════════════════════ */
.ska-panel {
    background: var(--ska-surface);
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-lg, 12px);
    box-shadow: var(--ska-shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.06));
}

.ska-input,
.ska-select {
    width: 100%;
    padding: 0.5rem 0.75rem;
    font-size: 0.875rem;
    color: var(--ska-text);
    background: var(--ska-surface);
    border: 1px solid var(--ska-border);
    border-radius: var(--ska-radius-md, 8px);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.ska-input:focus,
.ska-select:focus {
    outline: none;
    border-color: var(--ska-primary);
    box-shadow: 0 0 0 3px var(--ska-primary-glow);
}

.ska-input::placeholder {
    color: var(--ska-text-subtle);
}

/* Select: chevron propio (appearance:none quita la flecha nativa → la dibujamos
   con dos gradientes, idéntico al de los .jmf-field, sin SVG ni hex). */
.ska-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    cursor: pointer;
    padding-right: 2.1rem;
    background-image:
        linear-gradient(45deg, transparent 50%, var(--ska-text-muted) 50%),
        linear-gradient(135deg, var(--ska-text-muted) 50%, transparent 50%);
    background-position: calc(100% - 16px) calc(50% - 1px), calc(100% - 11px) calc(50% - 1px);
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
}

.ska-check {
    width: 1.05rem;
    height: 1.05rem;
    accent-color: var(--ska-primary);
    cursor: pointer;
}

/* Disabled: hace VISIBLE que el control está bloqueado (p. ej. "venta a crédito"
   deshabilitada hasta asignar cliente) — sin esto el checkbox se ve activo y "no responde". */
.ska-check:disabled {
    cursor: not-allowed;
    opacity: 0.45;
}

.ska-label {
    display: block;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--ska-text);
    margin-bottom: 0.25rem;
}

/* ──────────────────────────────────────────────────────────────────────────
   Helpers servidos — reemplazan utilidades Tailwind que NO están en el safelist
   precompilado de Unfold (space-y-*, opacity-*, font-mono, divide-y, tracking-*,
   text-7xl/8xl, shadow-xl/2xl…). Esas se purgan en silencio (no hay build de
   Tailwind del proyecto) → usar estas clases servidas en su lugar.
   ────────────────────────────────────────────────────────────────────────── */
.ska-admin-stack {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.ska-admin-stack--sm {
    gap: 12px;
}

.ska-admin-stack--xs {
    gap: 8px;
}

.ska-admin-divider {
    border: 0;
    border-top: 1px solid var(--ska-border);
    margin: 12px 0;
}

.ska-admin-muted {
    color: var(--ska-text-muted);
}

.ska-admin-subtle {
    color: var(--ska-text-subtle);
}

/* Numerales tabulares/monoespaciados (tickets, montos, ids) */
.ska-admin-num {
    font-variant-numeric: tabular-nums;
    font-family: var(--ska-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
}

/* Número/figura de display grande (KPIs) */
.ska-admin-display {
    font-size: 2.25rem;
    font-weight: 800;
    line-height: 1.1;
    color: var(--ska-text);
}

/* Empty-state (icono muted → texto), proporción según global-rules §3 */
.ska-admin-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 10px;
    padding: 40px 16px;
    color: var(--ska-text-muted);
}

.ska-admin-empty>i {
    font-size: 30px;
    opacity: 0.5;
}

.ska-admin-empty p {
    margin: 0;
    font-size: 14px;
}

/* Fila de metadatos (hero): chips separados, envuelve */
.ska-admin-meta-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 14px;
    font-size: 14px;
    color: var(--ska-text-subtle);
}

.ska-admin-meta-row>span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* Botones servidos. La BASE es el botón lleno (primario); `--ghost` es el secundario
   —volver, cancelar, abrir— y `--danger` el destructivo.

   HASTA EL 31-AGO-2026 LA BASE NO PINTABA NADA: declaraba el armado y, para el color,
   `border: 1px solid transparent` y nada más. Sin `background` y sin `color`, un botón
   sin modificador era texto pelado — invisible como botón. Lo reportó Sergio del tablero
   de inicio de prismo («el menú superior no tiene ningún estilo»), donde los cuatro
   accesos rápidos usan la base y los «Abrir» de cada bloque usan `--ghost`: los dos en
   la misma pantalla, a diez líneas, y sólo uno se veía.

   POR QUÉ EL PRIMARIO VA EN LA BASE Y NO EN UN `--primary` NUEVO, que es lo primero que
   uno piensa: los 15 usos sin modificador del árbol YA querían ser el primario, y se
   puede comprobar sin interpretar a nadie. Tres de ellos se habían pintado a mano con
   un `style="background: …"` inline —el POS del inicio de petnova y los dos del turno de
   caja—, y los otros doce están cada uno al lado de un hermano `--ghost` que hace de
   secundario: los tres «Confirmar» de `pet_360` tienen su «Cancelar» ghost en el MISMO
   `<form>`, «Editar» de editorial tiene «Ver en el blog» ghost al lado, y los `<noscript>`
   son el único botón de su formulario. Un `--primary` nuevo habría dejado los quince
   pidiendo el cambio uno por uno y la base seguiría siendo invisible para el próximo que
   la escriba — que es cómo esto llegó hasta acá.

   Los tres inline se sacan en el mismo commit: dos formas de pintar el mismo botón, y la
   inline gana siempre. Es cómo una de las dos empieza a mentir. */
.ska-admin-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 16px;
    border-radius: var(--ska-radius-md);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    /* La letra la CALCULA el motor del tema, que sabe de qué lado está el primario en
       cada modo. Escribirla `#ffffff` a mano es el bug ya arreglado en `.ska-btn--primary`
       (ver `test_letra_legible_sobre_la_marca.py`): en modo oscuro el motor ACLARA el
       primario para poder escribir CON él, y letra blanca encima da 1,6:1. */
    background: var(--ska-primary);
    color: var(--ska-text-on-primary, #fff);
    border: 1px solid var(--ska-primary);
    transition: background 0.15s, border-color 0.15s;
}

.ska-admin-btn:hover {
    /* El fondo del hover se ALEJA de la letra, nunca se le acerca — mismo token y mismo
       motivo que `.ska-btn--primary:hover`.

       ACÁ NO VA `color`, y no es un olvido. `.ska-admin-btn--ghost:hover` declara sólo
       `background` y `border-color`: si esta regla pusiera la letra del primario, el ghost
       NO la pisaría —tiene la misma especificidad pero no ese renglón— y los 66 botones
       ghost del árbol cambiarían de letra al pasar el mouse, quedando con la letra clara
       sobre `--ska-surface-alt`, que es casi blanco. La letra del primario ya la fija la
       base y en hover no cambia, así que repetirla acá sólo abriría esa puerta. Lo fija
       un test: `test_el_boton_del_admin_se_pinta.py`. */
    background: var(--ska-primary-hover, var(--ska-primary));
    border-color: var(--ska-primary-hover, var(--ska-primary));
}

/* El destructivo: hoy sólo el «Cerrar el turno» de la caja, que venía inline.

   La letra era `#fff` a propósito, con la medición escrita al lado y una tarjeta propia:
   se movió TAL CUAL desde el inline para que aquel commit no cambiara lo que se ve, y se
   dejó anotado que no había token que pedir. Ahora lo hay — el motor lo emite — así que
   este es el consumidor para el que se creó.

   Blanco sobre el fondo danger pasaba en 2 de 8 pares marca × modo; en modo oscuro fallaba
   en los cuatro, entre 2,27 y 2,74:1. Con la letra del motor pasa en 8 de 8.

   ⚠️ EL HOVER PIDE OTRO TOKEN, y no es un detalle de estilo: el fondo del hover NO es
   `--ska-danger` sino `--ska-danger-light`, que es un color distinto. Poner ahí la letra
   del base sería el mismo defecto una capa más abajo — medido, sobre `--ska-danger-light`
   en modo claro `--ska-text-on-danger` da 2,74:1 en petnova y prismo_so, porque el base
   `#DC2626` tolera blanco y el aclarado `#EA7C7C` no. Cada fondo pide SU letra. */
.ska-admin-btn--danger {
    background: var(--ska-danger);
    color: var(--ska-text-on-danger);
    border-color: var(--ska-danger);
}

.ska-admin-btn--danger:hover {
    background: var(--ska-danger-light);
    border-color: var(--ska-danger-light);
    color: var(--ska-text-on-danger-light);
}

.ska-admin-btn--ghost {
    background: var(--ska-surface);
    color: var(--ska-text);
    border-color: var(--ska-border);
}

.ska-admin-btn--ghost:hover {
    background: var(--ska-surface-alt);
    border-color: var(--ska-primary);
}

/* Imagen dentro del icono del hero del detalle */
.ska-admin-detail-icon img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: inherit;
}

/* Acciones a la derecha del hero del detalle */
.ska-admin-detail-header__actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
}

/* ══════════════════════════════════════════════════════════════════════════
   BARRIDO CSS MUERTO (scripts/check_dead_tailwind.py)
   Utilidades usadas por templates admin que el bundle PRECOMPILADO de Unfold
   purga (no hay build de Tailwind del proyecto → safelist fijo). Se DEFINEN acá
   para realizar la intención original sin tocar 54 templates (cero regresión).
   Las ska-* base (líneas 23-45) NO se repiten. Memoria: admin-sidebar...purgecss.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Tokens ska-* faltantes (bg/text/border + estados) ── */
.bg-ska-primary-glow {
    background-color: var(--ska-primary-glow) !important;
}

.bg-ska-danger {
    background-color: var(--ska-danger) !important;
}

.bg-ska-danger-bg {
    background-color: var(--ska-danger-bg) !important;
}

.bg-ska-success {
    background-color: var(--ska-success) !important;
}

.bg-ska-success-bg {
    background-color: var(--ska-success-bg) !important;
}

.bg-ska-warning {
    background-color: var(--ska-warning) !important;
}

.bg-ska-warning-bg {
    background-color: var(--ska-warning-bg) !important;
}

.bg-ska-text-muted {
    background-color: var(--ska-text-muted) !important;
}

.bg-ska-info-bg {
    background-color: var(--ska-info-bg) !important;
}

.bg-ska-primary-bg {
    background-color: var(--ska-primary-bg) !important;
}

.bg-ska-border {
    background-color: var(--ska-border) !important;
}

/* ── Las utilidades con una fracción en el nombre APLICAN esa fracción ──────────
   27-ago-2026. Hasta hoy las trece de acá abajo declaraban una opacidad en su NOMBRE
   y pintaban el color ENTERO, con `!important`:

       .bg-ska-danger\/10  { background-color: var(--ska-danger) !important; }

   El patrón con el que se usan es `bg-ska-X/10 text-ska-X` — un tinte suave con el
   color sólido encima. Con el fondo pintado sólido eso queda el MISMO color arriba y
   abajo: **contraste 1,00, invisible**. Medido con el motor real sobre las cuatro
   marcas y los dos temas, los 40 casos daban 1,00 clavado.

   Lo que lo hacía difícil de ver es que **el que mentía era el nombre de la clase, no
   la declaración**: quien audita el CSS lee `/10`, entiende «tinte al 10%» y sigue de
   largo. La vara de contraste tampoco lo cazaba, por lo mismo.

   `color-mix(… , transparent)` es el idioma que este archivo ya usaba para esto (ver
   las reglas de `--color-primary-600` más arriba), así que no estrena mecanismo.

   OJO CON LAS QUE NO ESTÁN ACÁ, y es a propósito: `.w-2\/12` y sus cuatro hermanas
   también llevan una fracción en el nombre, pero su propiedad es `width` y ahí la
   fracción YA está aplicada — son `width: 16.666667%`. Un arreglo que barra por «tiene
   `\/` en el selector» se las lleva puestas. Lo vigila `scripts/check_opacidad_en_el_nombre.py`.

   LO QUE ESTO NO ARREGLA, dicho con el número: con la opacidad puesta, un badge de
   icono `bg-ska-success/10 text-ska-success` pasa de 1,00 a 2,31 en claro. Deja de ser
   invisible, pero no llega al 3:1 de un componente gráfico. Eso NO es un defecto de
   estas reglas —es el patrón «tinte del color + el mismo color encima», que con esta
   paleta no da más— y vive en otras pantallas (xmessages, los diálogos). Está medido
   y abierto aparte; acá no se toca para no mezclar la causa raíz con un rediseño. */
.bg-ska-primary\/10 {
    background-color: color-mix(in srgb, var(--ska-primary) 10%, transparent) !important;
}

.bg-ska-danger\/10 {
    background-color: color-mix(in srgb, var(--ska-danger) 10%, transparent) !important;
}

.bg-ska-success\/10 {
    background-color: color-mix(in srgb, var(--ska-success) 10%, transparent) !important;
}

.bg-ska-warning\/10 {
    background-color: color-mix(in srgb, var(--ska-warning) 10%, transparent) !important;
}

.bg-ska-accent\/10 {
    background-color: color-mix(in srgb, var(--ska-accent) 10%, transparent) !important;
}

.text-ska-accent {
    color: var(--ska-accent) !important;
}

.text-ska-accent-amber {
    color: var(--ska-accent-amber) !important;
}

.text-ska-purple {
    color: var(--ska-purple) !important;
}

.text-ska-border {
    color: var(--ska-border) !important;
}

.text-ska-danger\/90 {
    color: color-mix(in srgb, var(--ska-danger) 90%, transparent) !important;
}

/* `--ska-danger` es un color de RELLENO: como letra no llega a 4,5:1 contra el panel
   del menú en NINGUNA de las cuatro marcas (4,39 en claro, 2,78 en oscuro, medido con
   los semánticos que siembran los productos). El motor ya genera el color de TEXTO
   equivalente —`--ska-badge-red-text`, `#991b1b` en claro y `#fca5a5` en oscuro, fijo
   para las cuatro marcas— pero no había utilidad que lo expusiera, así que un template
   no tenía cómo pedirlo. Con ella, «Terminar sesión» da 7,56 en claro y 5,50 en oscuro. */
.text-ska-badge-red-text {
    color: var(--ska-badge-red-text) !important;
}

/* Las tres hermanas de la de arriba. La misma historia y el mismo remedio, un escalón
   más allá: el badge de icono —`bg-ska-X/10` con el mismo color sólido encima— tampoco
   se lee. Con la opacidad ya aplicada da 1,99…4,13 en claro, o sea que NINGUNO de los
   20 casos llega al 4,5 de texto y la mayoría tampoco al 3:1 de un componente gráfico.
   Medido con el motor real sobre las 4 marcas y los 2 temas.

   Hasta acá el arreglo era INEJECUTABLE desde los templates, y por eso estas tres reglas
   no son un paso previo sino el hallazgo: los tokens existen los cuatro en
   `palettes/badges.py`, pero de utilidad sólo estaba la roja. Un template que pide una
   clase que no resuelve **no pinta nada y no levanta un solo error** — el icono se queda
   con el color heredado y la pantalla se ve normal.

   EL PRIMARIO NO USA EL PAR BADGE, y no es una inconsistencia: `primary` es color de
   MARCA y el par sólo existe en verde, rojo, azul y amarillo. Mapearlo a
   `--ska-badge-blue-text` da más contraste (7,71…7,97) pero le pinta el icono de AZUL a
   Taller Kids (naranja), Petnova (teal) y Nidova (verde). Decidido por Sergio el
   31-ago-2026: se conserva el matiz de la marca.

   Y va `--ska-link-hover`, NO `--ska-primary-dark`, que era lo primero que sale. Lo
   frenó `check_no_primary_dark_text.py` y tiene razón por un motivo que hay que saber
   antes de volver a intentarlo: en oscuro `--ska-primary-dark` es `lighten(primary, 0,3)`
   mientras `--ska-primary` es `lighten(primary, 0,5)`, así que como LETRA queda menos
   claro que el texto normal sobre una superficie oscura — contraste que baja justo donde
   se lo quiere subir. `--ska-link-hover` es `darken(primary, 0,3)` en claro y
   `lighten(primary, 0,7)` en oscuro: se aleja de la superficie en los DOS modos y
   conserva el matiz. Medido sobre `bg-ska-primary/10` en las 4 marcas:

                            claro         oscuro
       --ska-primary-dark   3,88 … 6,22   4,44 … 6,14
       --ska-link-hover     4,40 … 6,83   8,19 … 8,89   ← el que se usa

   ⚠️ EL 1-SEP-2026 SE INTENTÓ SUBIRLO A `darken 0,6` Y SE REVIRTIÓ, porque este token
   hace DOS trabajos y sólo uno se ve desde acá: además del hover de un link, pinta el
   icono de este badge en REPOSO. Hundirlo subía el peor badge de 4,40 a 6,34 —más
   contraste, sí— llevándose matiz de marca puesto, que es el eje exacto sobre el que se
   decidió arriba conservar el matiz. Lo cazó el trinquete de
   `test_el_badge_de_icono_YA_LLEGA_A_TRES_A_UNO_EN_CLARO`, y es la razón por la que el
   hover del TEXTO de marca terminó en su propio token (`--ska-primary-text-hover`) en
   vez de reusar éste. La tabla de arriba sigue vigente. */
.text-ska-badge-green-text {
    color: var(--ska-badge-green-text) !important;
}

.text-ska-badge-yellow-text {
    color: var(--ska-badge-yellow-text) !important;
}

/* La base de `.hover\:text-ska-link-hover:hover`, que ya existía sola: la variante de
   cursor estaba y la clase sin estado no, así que un template no podía pedir el color de
   marca legible salvo al pasar el mouse por encima. */
.text-ska-link-hover {
    color: var(--ska-link-hover) !important;
}

.border-ska-border-light {
    border-color: var(--ska-border-light) !important;
}

.border-ska-accent {
    border-color: var(--ska-accent) !important;
}

.border-ska-danger {
    border-color: var(--ska-danger) !important;
}

.border-ska-success {
    border-color: var(--ska-success) !important;
}

.border-ska-success-border {
    border-color: var(--ska-success-border) !important;
}

.border-ska-warning {
    border-color: var(--ska-warning) !important;
}

.border-ska-danger\/30 {
    border-color: color-mix(in srgb, var(--ska-danger) 30%, transparent) !important;
}

.border-ska-success\/30 {
    border-color: color-mix(in srgb, var(--ska-success) 30%, transparent) !important;
}

.border-ska-warning\/30 {
    border-color: color-mix(in srgb, var(--ska-warning) 30%, transparent) !important;
}

.border-ska-border\/40 {
    border-color: color-mix(in srgb, var(--ska-border) 40%, transparent) !important;
}

.divide-ska-border> :not([hidden])~ :not([hidden]) {
    border-color: var(--ska-border) !important;
}

.hover\:bg-ska-danger-bg:hover {
    background-color: var(--ska-danger-bg) !important;
}

.hover\:bg-ska-success-bg:hover {
    background-color: var(--ska-success-bg) !important;
}

.hover\:bg-ska-danger\/10:hover {
    background-color: color-mix(in srgb, var(--ska-danger) 10%, transparent) !important;
}

.hover\:bg-ska-danger\/85:hover {
    background-color: color-mix(in srgb, var(--ska-danger) 85%, transparent) !important;
}

.hover\:bg-ska-warning\/85:hover {
    background-color: color-mix(in srgb, var(--ska-warning) 85%, transparent) !important;
}

.hover\:text-ska-text:hover {
    color: var(--ska-text) !important;
}

/* Hover de un link pintado con la marca. El reposo de ese link es `.text-ska-primary`,
 * que desde el 1-sep-2026 consume `--ska-primary-text` (darken 0,35 en claro).
 *
 * Antes usaba --ska-primary-dark, que en modo oscuro vale lighten(primary, 0.3)
 * mientras el base vale 0.5: el hover quedaba MENOS claro que el texto normal
 * sobre fondo oscuro, o sea el feedback al revés. Después usó --ska-link-hover, que
 * se aleja de la SUPERFICIE en los dos modos y arreglaba eso.
 *
 * Y AHORA USA `--ska-primary-text-hover`, POR EL MISMO DEFECTO UNA TERCERA VEZ, en la
 * otra punta: cuando el reposo se hundió a 0,35 para llegar a 4,5:1, `--ska-link-hover`
 * (0,30) quedó MÁS CLARO que su propio reposo — pasar el mouse aclaraba el link, en las
 * cinco marcas. No se arregló subiendo `--ska-link-hover` porque ese token también pinta
 * el ICONO de los badges en reposo, y moverlo cambiaba cuánto matiz de marca se lleva
 * puesto un badge: una decisión que Sergio tomó el 31-ago-2026 y que no es de este
 * arreglo. El par reposo/hover del TEXTO vive ahora en sus dos tokens propios.
 *
 * La lección, que ya costó tres vueltas: este hover no se elige mirándolo a él. Se elige
 * MIRANDO SU REPOSO, y los dos se mueven juntos. Ver
 * test_el_color_de_marca_escrito_se_lee.py. */
.hover\:text-ska-link-hover:hover {
    color: var(--ska-primary-text-hover) !important;
}

.hover\:text-ska-danger:hover {
    color: var(--ska-danger) !important;
}

.hover\:border-ska-border:hover {
    border-color: var(--ska-border) !important;
}

.hover\:border-ska-danger:hover {
    border-color: var(--ska-danger) !important;
}

.hover\:border-ska-success:hover {
    border-color: var(--ska-success) !important;
}

.hover\:border-ska-warning:hover {
    border-color: var(--ska-warning) !important;
}

.hover\:border-ska-surface-alt:hover {
    border-color: var(--ska-surface-alt) !important;
}

.focus\:border-ska-primary:focus {
    border-color: var(--ska-primary) !important;
}

.focus\:ring-ska-primary:focus {
    box-shadow: 0 0 0 2px var(--ska-primary) !important;
}

.focus\:ring-ska-border:focus {
    box-shadow: 0 0 0 2px var(--ska-border) !important;
}

.focus\:ring-offset-ska-surface:focus {
    --tw-ring-offset-color: var(--ska-surface) !important;
}

.group:hover .group-hover\:text-ska-primary {
    color: var(--ska-primary-text) !important;
}

.group:hover .group-hover\:border-ska-primary {
    border-color: var(--ska-primary) !important;
}

/* ── Compat Tailwind v3 (valores default; colores → tokens ska-*) ── */
.space-y-1> :not([hidden])~ :not([hidden]) {
    margin-top: 0.25rem;
}

.space-y-3> :not([hidden])~ :not([hidden]) {
    margin-top: 0.75rem;
}

.space-y-4> :not([hidden])~ :not([hidden]) {
    margin-top: 1rem;
}

.space-y-6> :not([hidden])~ :not([hidden]) {
    margin-top: 1.5rem;
}

.divide-y> :not([hidden])~ :not([hidden]) {
    border-top-width: 1px;
}

/* ── Pasos de media unidad (`.5`) ──────────────────────────────────────────────
   El CSS de Unfold viene precompilado y PURGADO: sólo trae las clases que usan
   SUS plantillas. Las de media unidad que sólo usan las plantillas de este repo
   no tienen regla y por lo tanto no hacen NADA — se ven como cero, en silencio.

   Medido contra `/app/static/unfold/css/styles.css` el 2026-08-13: de las 23 en
   uso, 12 sobreviven (Unfold las usa) y estas 11 no existían en ningún CSS. Eran
   125 declaraciones muertas repartidas por el admin de los cuatro productos; la
   que se veía era `gap-2.5`, que dejaba los badges de "Sync Services" pegados.

   Van acá, en un solo lugar, y no reescribiendo 40 plantillas: el problema es que
   la regla no existe, no que la clase esté mal elegida. Valores por defecto de
   Tailwind (0.5→0.125rem, 1.5→0.375rem, 2.5→0.625rem, 3.5→0.875rem). El punto va
   escapado porque en CSS `.` empieza un selector de clase. */
.gap-2\.5 { gap: 0.625rem; }

.mb-0\.5 { margin-bottom: 0.125rem; }

.mt-1\.5 { margin-top: 0.375rem; }

.p-2\.5 { padding: 0.625rem; }

.pb-1\.5 { padding-bottom: 0.375rem; }

.pt-1\.5 { padding-top: 0.375rem; }

.py-0\.5 { padding-top: 0.125rem; padding-bottom: 0.125rem; }

.py-3\.5 { padding-top: 0.875rem; padding-bottom: 0.875rem; }

.space-y-1\.5> :not([hidden])~ :not([hidden]) { margin-top: 0.375rem; }

.top-2\.5 { top: 0.625rem; }

.w-1\.5 { width: 0.375rem; }

.opacity-5 {
    opacity: 0.05;
}

.opacity-20 {
    opacity: 0.2;
}

.opacity-30 {
    opacity: 0.3;
}

.opacity-40 {
    opacity: 0.4;
}

.opacity-50 {
    opacity: 0.5;
}

.opacity-60 {
    opacity: 0.6;
}

.opacity-70 {
    opacity: 0.7;
}

.opacity-80 {
    opacity: 0.8;
}

.shadow-none {
    box-shadow: 0 0 #0000;
}

.shadow-md {
    box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
}

.shadow-xl {
    box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
}

.shadow-2xl {
    box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.25);
}

.tracking-wide {
    letter-spacing: 0.025em;
}

.tracking-wider {
    letter-spacing: 0.05em;
}

.tracking-widest {
    letter-spacing: 0.1em;
}

.text-7xl {
    font-size: 4.5rem;
    line-height: 1;
}

.text-8xl {
    font-size: 6rem;
    line-height: 1;
}

.text-\[8px\] {
    font-size: 8px;
}

.text-\[9px\] {
    font-size: 9px;
}

.text-\[10px\] {
    font-size: 10px;
}

.text-\[13px\] {
    font-size: 13px;
}

.font-mono {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}

.font-black {
    font-weight: 900;
}

.leading-tight {
    line-height: 1.25;
}

.w-11 {
    width: 2.75rem;
}

.w-12 {
    width: 3rem;
}

.w-14 {
    width: 3.5rem;
}

.w-28 {
    width: 7rem;
}

.w-64 {
    width: 16rem;
}

.w-2\/12 {
    width: 16.666667%;
}

.w-3\/12 {
    width: 25%;
}

.w-4\/12 {
    width: 33.333333%;
}

.w-5\/12 {
    width: 41.666667%;
}

.w-6\/12 {
    width: 50%;
}

.h-11 {
    height: 2.75rem;
}

.h-14 {
    height: 3.5rem;
}

.h-48 {
    height: 12rem;
}

.max-h-48 {
    max-height: 12rem;
}

.max-h-56 {
    max-height: 14rem;
}

.max-h-60 {
    max-height: 15rem;
}

.max-h-64 {
    max-height: 16rem;
}

.max-h-\[600px\] {
    max-height: 600px;
}

.max-w-xs {
    max-width: 20rem;
}

.max-w-md {
    max-width: 28rem;
}

.max-w-lg {
    max-width: 32rem;
}

.max-w-\[120px\] {
    max-width: 120px;
}

.max-w-\[200px\] {
    max-width: 200px;
}

.min-w-\[150px\] {
    min-width: 150px;
}

.min-w-\[200px\] {
    min-width: 200px;
}

.z-\[9999\] {
    z-index: 9999;
}

.flex-\[2\] {
    flex: 2 2 0%;
}

.flex-shrink-0 {
    flex-shrink: 0;
}

.col-span-full {
    grid-column: 1 / -1;
}

.items-end {
    align-items: flex-end;
}

.items-baseline {
    align-items: baseline;
}

.self-end {
    align-self: flex-end;
}

.gap-0 {
    gap: 0;
}

.gap-x-6 {
    column-gap: 1.5rem;
}

.gap-y-2 {
    row-gap: 0.5rem;
}

.gap-y-4 {
    row-gap: 1rem;
}

.rounded-b-lg {
    border-bottom-left-radius: 0.5rem;
    border-bottom-right-radius: 0.5rem;
}

.rounded-bl-full {
    border-bottom-left-radius: 9999px;
}

.rounded-r-full {
    border-top-right-radius: 9999px;
    border-bottom-right-radius: 9999px;
}

.outline-none {
    outline: 2px solid transparent;
    outline-offset: 2px;
}

.overflow-y-hidden {
    overflow-y: hidden;
}

.inset-y-0 {
    top: 0;
    bottom: 0;
}

.border-dotted {
    border-style: dotted;
}

.border-opacity-50 {
    --tw-border-opacity: 0.5;
}

.text-gray-200 {
    color: var(--ska-text-muted) !important;
}

.text-gray-400 {
    color: var(--ska-text-subtle) !important;
}

.text-gray-700 {
    color: var(--ska-text-secondary) !important;
}

.bg-black\/40 {
    background-color: rgb(0 0 0 / 0.4);
}

.border-black\/5 {
    border-color: rgb(0 0 0 / 0.05);
}

.transition-shadow {
    transition-property: box-shadow;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    transition-duration: 150ms;
}

.duration-150 {
    transition-duration: 150ms;
}

.duration-200 {
    transition-duration: 200ms;
}

.bg-gradient-to-l {
    background-image: linear-gradient(to left, var(--tw-gradient-stops));
}

@keyframes ska-spin {
    to {
        transform: rotate(360deg);
    }
}

.animate-spin-slow {
    animation: ska-spin 3s linear infinite;
}

.hover\:opacity-80:hover {
    opacity: 0.8;
}

.hover\:opacity-90:hover {
    opacity: 0.9;
}

.hover\:underline:hover {
    text-decoration-line: underline;
}

.disabled\:opacity-40:disabled {
    opacity: 0.4;
}

.focus\:ring-2:focus {
    box-shadow: 0 0 0 2px currentColor;
}

.focus\:ring-offset-2:focus {
    outline-offset: 2px;
}

@media (min-width: 640px) {
    .sm\:grid-cols-2 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .sm\:grid-cols-3 {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (min-width: 768px) {
    .md\:hidden {
        display: none;
    }

    .md\:justify-between {
        justify-content: space-between;
    }

    .md\:text-right {
        text-align: right;
    }
}

/* ── Triage (hooks/legacy) ── */
.table-responsive {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.ring-1 {
    box-shadow: 0 0 0 1px var(--tw-ring-color, var(--ska-primary));
}

.ring-primary-500 {
    --tw-ring-color: var(--ska-primary);
}
/* ═══════════════════════════════════════════════════
   ACCIONES DE FILA + IDENTIFICADOR CORTO DE LA CITA
   ═══════════════════════════════════════════════════

   UN solo botón de acción de fila para las CUATRO superficies que muestran una cita (dashboard,
   agenda lista, calendario, turno de caja). Antes cada una tenía el suyo y sólo la del dashboard
   era usable; unificarlas es justamente el pedido de la tarjeta.

   EL TAMAÑO NO ES ESTÉTICA, ES EL BUG. Lo que había eran glifos con `padding: 2px` separados por
   `margin-left: 6px` (destino real ≈ 18 px) y, en el calendario, 20 px con `opacity: 0` hasta el
   hover. WCAG 2.2 SC 2.5.8 (AA) exige 24×24 px CSS de destino, O BIEN 24 px de separación entre
   destinos vecinos: fallaba las dos condiciones a la vez. Material pide además ≥8 dp de separación
   y expandir el área táctil más allá del glifo. 32 px de destino con `gap: 8px` pasa ambas con
   aire, sin agrandar el ícono (que sigue a 14 px) ni ensanchar la fila.

   VISIBLES SIEMPRE. Revelar en hover se ve limpio y rompe la descubribilidad — y en una pantalla
   táctil el hover no existe, así que la acción simplemente no está. Por eso acá no hay `opacity: 0`
   en ningún lado: el hover sólo SUBE el contraste de un botón que ya se veía.

   RÓTULO REAL, no `title=`. El tooltip no lo lee un lector de pantalla como nombre accesible ni
   aparece en táctil. Cada botón lleva su `aria-label` en el markup; acá sólo va la forma. */
.ska-row-actions {
    display: inline-flex;
    align-items: center;
    gap: 8px;               /* ≥8 dp entre destinos vecinos (Material) */
    justify-content: flex-end;
    white-space: nowrap;
}

.ska-row-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;            /* > 24×24 px CSS (WCAG 2.2 SC 2.5.8, AA) */
    height: 32px;
    padding: 0;
    border: 1px solid var(--ska-border);
    border-radius: 8px;
    background: var(--ska-surface);
    /* --ska-text-secondary (NEUTRAL 700), NO --ska-text-subtle (NEUTRAL 400, que la paleta define
       como "disabled, placeholders"): el ícono anterior iba en --ska-text-muted (500) y se leía como
       un fantasma gris. Un botón activo no puede tener el color de uno deshabilitado. */
    color: var(--ska-text-secondary);
    font-size: 14px;        /* el GLIFO sigue chico: lo que crece es el área de clic */
    line-height: 1;
    cursor: pointer;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}

.ska-row-action:hover {
    background: var(--ska-surface-alt);
    color: var(--ska-text);
    border-color: var(--ska-border-hover);
}

/* Foco VISIBLE con teclado: estos botones cambian el estado de una cita y cobran plata; navegarlos
   a ciegas con Tab es cómo se marca "No asistió" en la fila equivocada. */
.ska-row-action:focus-visible {
    outline: 2px solid var(--ska-primary);
    outline-offset: 2px;
}

.ska-row-action[disabled] {
    opacity: .45;
    cursor: not-allowed;
}

/* COBRAR es la acción de PLATA: se tiñe de la marca para que no sea "uno más de los cuatro íconos".
   Es la única del grupo que el operador busca activamente. */
.ska-row-action--primary {
    color: var(--ska-primary);
    border-color: var(--ska-primary);
}

.ska-row-action--primary:hover {
    background: var(--ska-primary);
    color: var(--ska-text-on-primary);
    border-color: var(--ska-primary);
}

/* DESTRUCTIVAS (No asistió / Cancelar): rojas al hover, y separadas del grupo por la línea de abajo.
   Van al final y aparte porque son las que no se deshacen desde esta pantalla. */
.ska-row-action--danger:hover {
    background: var(--ska-danger);
    /* El motor la calcula contra el fondo de CADA modo. Con `#fff` fijo fallaba en 6 de
       los 8 pares marca × modo, y en oscuro en los CUATRO (2,27 a 2,74:1) — justo donde
       la acción destructiva tiene que leerse mejor, no peor. */
    color: var(--ska-text-on-danger);
    border-color: var(--ska-danger);
}

/* Variante CON RÓTULO — para donde sí hay ancho (el panel lateral del calendario). NN/g: el ícono
   solo se reconoce mal salvo un puñado universal (home, imprimir, lupa); ninguno de estos cuatro lo
   es. Donde el rótulo no entra (una fila de 10 columnas) el nombre sigue en `aria-label`. */
.ska-row-actions--labelled {
    flex-wrap: wrap;
    justify-content: flex-start;
}

.ska-row-actions--labelled .ska-row-action {
    width: auto;
    min-width: 32px;
    gap: 6px;
    padding: 0 12px;
    font-size: .8125rem;
    font-weight: 600;
}

/* Separador entre las acciones normales y las destructivas: 1 px de línea + el gap de 8 a cada
   lado ⇒ 17 px reales entre el último botón seguro y el primero peligroso. */
.ska-row-actions__sep {
    width: 1px;
    height: 20px;
    background: var(--ska-border);
    flex: 0 0 auto;
}

/* En pantallas táctiles no hay hover que revele nada y el dedo no apunta como el mouse: el destino
   sube a 44 px, que es el mínimo cómodo de las guías táctiles (y el AAA de WCAG 2.5.5). */
@media (hover: none) and (pointer: coarse) {
    .ska-row-actions:not(.ska-row-actions--labelled) .ska-row-action {
        width: 44px;
        height: 44px;
    }
    /* El rotulado ya es ancho por su texto: sólo sube el alto (si le forzáramos `width` volvería a
       ser cuadrado y le cortaría el rótulo). */
    .ska-row-actions--labelled .ska-row-action {
        height: 44px;
    }
}

/* ── Identificador corto de la cita (KTD) ──
   Monoespaciado y con `tracking`: se lee letra por letra para dictarlo por teléfono, que es para lo
   que existe. Es el MISMO string en las cuatro pantallas (scheduling/short_code.py). */
.sch-code {
    display: inline-block;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .06em;
    padding: 2px 6px;
    border-radius: 6px;
    background: var(--ska-surface-alt);
    color: var(--ska-text-secondary);
    white-space: nowrap;
}

.sch-code--link {
    text-decoration: none;
}

.sch-code--link:hover {
    background: var(--ska-primary-glow);
    color: var(--ska-primary);
}

.sch-code--link:focus-visible {
    outline: 2px solid var(--ska-primary);
    outline-offset: 2px;
}

/* ─────────────────────────────────────────────────────────────────────────────
   TOOLBAR DE PERSONAS — pestañas, chips de rol y botones de alta.
   La pinta admin/includes/_ska_people_toolbar.html en los changelist de «Usuarios»
   de los tres productos. Todo con tokens --ska-*: cero hex (regla del design system).
   ───────────────────────────────────────────────────────────────────────────── */

.ska-people-toolbar {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 14px;
}

.ska-people-toolbar__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

/* ── Pestañas a otros listados (mascotas, proveedores, recursos) ── */
.ska-tabbar {
    display: flex;
    align-items: center;
    gap: 2px;
    flex-wrap: wrap;
    border-bottom: 1px solid var(--ska-border);
}

.ska-tabbar__tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    font-size: .875rem;
    font-weight: 600;
    color: var(--ska-text-secondary);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    white-space: nowrap;
}

.ska-tabbar__tab:hover {
    color: var(--ska-text);
    background: var(--ska-surface-alt);
}

.ska-tabbar__tab--active {
    color: var(--ska-primary);
    border-bottom-color: var(--ska-primary);
}

/* ── Chips de rol: filtran la lista. Multi-valor por fila, ver people_role_registry ── */
.ska-rolechips {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.ska-rolechip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 12px;
    border-radius: 999px;
    font-size: .8125rem;
    font-weight: 600;
    text-decoration: none;
    border: 1px solid var(--ska-border);
    background: var(--ska-surface);
    color: var(--ska-text-secondary);
    white-space: nowrap;
}

.ska-rolechip:hover {
    border-color: var(--ska-primary);
    color: var(--ska-primary);
}

/* Chip ACTIVO: el color pasa de ser la letra a ser el FONDO, así que la letra la tiene que
   decir el motor. Cada uno pide SU token, y por eso la regla agrupada que ponía `color:#fff`
   a los cuatro dejó de existir: era la que hacía el daño. Medido antes de sacarla, letra
   blanca sobre el fondo que sirve cada modo, en las cuatro marcas — warning falla los OCHO
   pares (1,62:1 en taller-kids oscuro, que es el peor de todo el sistema) y success también;
   info falla seis. En modo oscuro fallan los cuatro semánticos en las cuatro marcas, porque
   el motor ACLARA el color a propósito para que se vea sobre superficie oscura. */
.ska-rolechip--active {
    background: var(--ska-primary);
    border-color: var(--ska-primary);
    /* Era `var(--ska-on-primary, #fff)`, y ese token NUNCA existió: el motor emite
       `--ska-text-on-primary`. La regla se leía como cableada al tema y caía siempre al
       fallback blanco — el mismo defecto que este commit cierra, con otro nombre. */
    color: var(--ska-text-on-primary);
}

.ska-rolechip--info    { border-color: var(--ska-info);    color: var(--ska-info); }
.ska-rolechip--success { border-color: var(--ska-success); color: var(--ska-success); }
.ska-rolechip--warning { border-color: var(--ska-warning); color: var(--ska-warning); }

.ska-rolechip--info.ska-rolechip--active    { background: var(--ska-info);    border-color: var(--ska-info);    color: var(--ska-text-on-info); }
.ska-rolechip--success.ska-rolechip--active { background: var(--ska-success); border-color: var(--ska-success); color: var(--ska-text-on-success); }
.ska-rolechip--warning.ska-rolechip--active { background: var(--ska-warning); border-color: var(--ska-warning); color: var(--ska-text-on-warning); }

/* ── Chip de rol EN LA FILA (no filtra; describe). La pinta cada card del producto. ── */
.ska-rolebadge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: .6875rem;
    font-weight: 700;
    background: var(--ska-surface-alt);
    color: var(--ska-text-secondary);
    border: 1px solid var(--ska-border);
    white-space: nowrap;
}

.ska-rolebadge--info    { color: var(--ska-info);    border-color: var(--ska-info); }
.ska-rolebadge--success { color: var(--ska-success); border-color: var(--ska-success); }
.ska-rolebadge--warning { color: var(--ska-warning); border-color: var(--ska-warning); }

/* ── Botón de alta. Un solo estilo para los tres que convivían: el redondo de Unfold,
      el Tailwind inline de interak y el sk-usr-btn de users (con CSS copiado en 4 productos). ── */
.ska-people-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.ska-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 16px;
    border-radius: var(--ska-radius-md, 10px);
    font-size: .875rem;
    font-weight: 600;
    text-decoration: none;
    border: 1px solid var(--ska-border);
    background: var(--ska-surface);
    color: var(--ska-text);
    white-space: nowrap;
    cursor: pointer;
}

/* `:not(.ska-btn--primary)` y no un simple `.ska-btn:hover`: las dos reglas tenían la
   MISMA especificidad, así que el botón primario heredaba este fondo claro —y su propia
   regla de hover sólo tocaba `filter` y `color`, nunca el fondo. Resultado en pantalla:
   letra blanca sobre un gris casi blanco. El botón no se veía feo, DESAPARECÍA. */
.ska-btn:not(.ska-btn--primary):hover {
    background: var(--ska-surface-alt);
}

/* ── UN botón primario, una sola regla ─────────────────────────────────────────────
   El selector va agrupado a propósito. En el admin conviven DOS marcados para el mismo
   botón: el nuestro (`.ska-btn--primary`, la barra de personas) y el de Unfold, que pinta
   «Grabar» con utilidades de Tailwind (`bg-primary-600 text-white`) desde su propio
   paquete. No se puede unificar por variable: Unfold escribe `text-white` LITERAL en
   `unfold/components/button.html`, así que ninguna variable del tema lo alcanza.

   Lo que sí se unifica es el criterio: un solo fondo (`--ska-primary`, el color exacto de
   la marca) y una sola letra (`--ska-text-on-primary`, calculada por luminancia en el
   motor del tema). Antes había tres fondos distintos —500 acá, 600 en Unfold, y el
   aclarado en modo oscuro— cada uno con blanco encima, y los ocho combinados fallaban el
   mínimo de contraste.

   `button`/`a`/`input` en el selector de Unfold: `.bg-primary-600` también se usa en
   chips y barras de progreso, que no llevan texto encima y no deben cambiar.

   ⚠️ Esos tres prefijos ACOTAN, no ganan — y conviene saberlo porque lo natural es leerlos
   al revés. Este bloque le gana a `.text-white` de Unfold por la CAPA, no por la
   especificidad: el CSS de Unfold es Tailwind v4 y sus utilidades viven en
   `@layer utilities`, este archivo no DECLARA ninguna capa —este comentario la nombra, que
   es otra cosa; el comando que los distingue está en el doc—, y la cascada
   compara la capa ANTES que la especificidad. Medido con getComputedStyle en Chromium 149
   el 2026-08-27 (`AlJuOLg7`): un `button { color: … }` nuestro —0-0-1, MENOS específico que
   `.text-white`, 0-1-0— también gana. Sin los prefijos este bloque seguiría ganando; lo que
   pasaría es que además mordería los `<div>` de chips y progreso. La tabla entera y las dos
   consecuencias para quien escribe un template están en `docs/admin/global-rules.md` §2.

   Y por eso este selector NO se saca aunque los cuatro `bg-primary-600` del árbol se
   reescriban: 13 sitios del paquete Unfold producen un `button`/`a`/`input` con esa clase y
   nadie de acá los escribe — el «Grabar» de la pantalla de cambio de TODOS los modelos
   (`unfold/templates/admin/submit_line.html:8`) entre ellos. Contado el 2026-08-27 sobre las
   apps de INSTALLED_APPS (`unfold` + `unfold.contrib.forms`). */
.ska-btn--primary,
button.bg-primary-600,
a.bg-primary-600,
input.bg-primary-600 {
    background: var(--ska-primary);
    border-color: var(--ska-primary);
    color: var(--ska-text-on-primary, #fff);
}

/* ── La SEGUNDA puerta: los `div`/`span` de Unfold que también llevan texto encima ──
   El bloque de arriba acota a `button`/`a`/`input` y su comentario justifica el recorte con
   que «`.bg-primary-600` también se usa en chips y barras de progreso, que no llevan texto
   encima». Eso es cierto para 5 de los 10 sitios que NO son `button`/`a`/`input`. Para los
   otros 5 es falso: sí llevan texto, y se quedaban con el `text-white` LITERAL de Unfold.

   POR QUÉ NO SE AGREGA `div.bg-primary-600` Y LISTO, que es lo primero que uno piensa: eso
   mordería los cinco `<div>` decorativos —`progress.html:22` y `:25` (barras de progreso),
   `table.html:54`, `change_list_results.html:37` y `:54` (las guías de fila)—, que son tiras
   de 2px sin una letra adentro y que NO deben cambiar de color. Un arreglo que las muerda es
   peor que el defecto.

   LO QUE DISCRIMINA, y no es una enumeración de rutas: **el propio `text-white` de Unfold**.
   Unfold escribe esa clase exactamente donde declara que hay texto sobre el primario, y en
   ninguno de los cinco decorativos. Así que el selector le pregunta a Unfold por su propia
   intención en vez de listar archivos de un paquete que se actualiza. Contado el 27-ago-2026
   sobre `unfold` + `unfold.contrib.forms` (los dos únicos en INSTALLED_APPS):

       CON texto → entran            array.html:29 (un <div> con x-on:click: es un BOTÓN)
                                     inline_add.html:7 (el chip «+» de un inline)
                                     site_icon.html:16 (la <span> del ícono, con site_dropdown)
       SIN texto → quedan afuera     progress.html:22 · progress.html:25 · table.html:54
                                     change_list_results.html:37 · change_list_results.html:54

   `actions.html:4` va por el segundo selector y es el único que necesita uno propio: ahí el
   `bg-primary-600` está en el hijo y el `text-white` en el padre (`#changelist-actions`), así
   que el color se pone donde está el fondo y baja por herencia. Los dos elementos de adentro
   que tienen fondo propio —el botón «Run» (`bg-primary-900`) y los enlaces de «seleccionar
   todo» (`bg-white/20`)— llevan su propio `text-white` y por eso no los alcanza.

   `hijack/notification.html:4` también lleva texto, y NO está acá a propósito:
   `unfold.contrib.hijack` no está en INSTALLED_APPS, así que ese marcado no se pinta nunca.
   Si algún día se instala, el primer selector lo toma solo — tiene `text-white`.

   MEDIDO sobre los 9 tenants VIVOS de producción (27-ago-2026, `prod.sh sql`, READ ONLY;
   los `primary` reales, no el `DESIGN_SYSTEM` del árbol), con `ThemeEngine.contraste` y
   `ThemeEngine.texto_sobre`, vara 4.5 —los tres sitios son texto normal: 16px/400, 14px/500
   y 16px/400—:

       pasan la vara     HOY 10 de 18 pares (tenant × modo)  →  CON ESTO 17 de 18

   EL PAR QUE NO MEJORA, y va escrito porque el arreglo lo EMPEORA en una punta: petnova org 4
   (`#368F3F`) en claro pasa de 5.91 con blanco a 4.35 con la letra del motor. No es un defecto
   de esta regla: es que ese verde es un tono medio que queda lejos del blanco Y lejos del
   negro —`_tokens_sobre_primario` ya lo detecta y lo AVISA en el arranque («el primario no
   alcanza contraste legible con NINGUNA letra»)—, y su propio botón «Grabar» ya está en 4.35
   hoy por el bloque de arriba. O sea que esto no introduce el problema: hace que la barra de
   acciones diga lo mismo que el botón que tiene al lado. Eso se arregla cambiando la marca,
   no el CSS. */
.bg-primary-600.text-white,
#changelist-actions > .bg-primary-600 {
    background: var(--ska-primary);
    color: var(--ska-text-on-primary, #fff);
}

.ska-btn--primary:hover,
button.bg-primary-600:hover,
a.bg-primary-600:hover,
input.bg-primary-600:hover {
    /* El fondo del hover lo calcula el motor del tema, que sabe de qué lado está la letra:
       se ALEJA de ella, nunca se le acerca. El bug original era `filter: brightness(1.08)`,
       que aclara pase lo que pase — sobre un botón de letra blanca eso lo empeora. */
    background: var(--ska-primary-hover, var(--ska-primary));
    border-color: var(--ska-primary-hover, var(--ska-primary));
    color: var(--ska-text-on-primary, #fff);
}

.ska-btn:focus-visible {
    outline: 2px solid var(--ska-primary);
    outline-offset: 2px;
}

/* UN BOTÓN APAGADO TIENE QUE VERSE APAGADO — y no puede depender de que lo escriban.

   El 12-ago-2026 la caja de Pet Club estuvo detenida por un «Cobrar» deshabilitado sin señal.
   Al revisar la migración de botones a este patrón salió que `.ska-btn` no tenía NINGUNA regla
   `:disabled`: el gris venía de una utilidad (`disabled:opacity-40`) escrita a mano en cada
   `class`. O sea que el estado apagado sobrevivía sólo mientras nadie se olvidara de copiarla —
   y el día que alguien la olvide, el botón queda muerto con cara de vivo. Eso es peor que el
   incidente original: el cajero aprieta, no pasa nada, y no hay nada que leer.

   Vive en el patrón para que sea el defecto y no una decoración opcional. `cursor: not-allowed`
   además lo delata antes del click, que es cuando todavía sirve saberlo.

   NO lleva `pointer-events: none`, y es deliberado: un `<button disabled>` ya no dispara clicks,
   así que no aporta nada — pero apaga el `:hover`, y con él el tooltip del `title`. Ese `title` es
   donde el «Cobrar» escribe POR QUÉ está apagado. Ponerlo sería tapar la explicación con el mismo
   gesto que pinta el candado. */
.ska-btn:disabled,
.ska-btn[disabled],
.ska-btn[aria-disabled="true"] {
    opacity: 0.4;
    cursor: not-allowed;
}

/* ── Franja de suplantación («estás operando como otra persona») ────────────────────────────────
 * Va en ESTE archivo y no como clases Tailwind en el template por el gotcha de PurgeCSS: el CSS de
 * Unfold viene pre-compilado y purgado contra SUS plantillas, así que una clase que sólo usa un
 * override del repo se queda sin regla y no se ve. Acá la regla es nuestra y siempre existe.
 *
 * Usa `--ska-warning` y no el color de marca: la marca cambia por tenant y hay tenants con
 * primarios casi negros (Pet Club), donde una franja de marca se confunde con el chrome. Esto tiene
 * que gritar en los cuatro productos, no combinar.
 */
.ska-impersonation-bar {
    position: sticky;
    top: 0;
    z-index: 60;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    padding: 0.5rem 1rem;
    background-color: var(--ska-warning);
    color: #1a1a1a;
    font-size: 0.8125rem;
    font-weight: 600;
    line-height: 1.3;
    text-align: center;
}

/* El botón de volver hereda la tipografía de la franja y se separa del fondo con un borde, no con
 * otro color: sobre un ámbar fuerte, un segundo color de relleno compite y se lee peor. */
.ska-impersonation-bar__back {
    padding: 0.125rem 0.625rem;
    border: 1px solid currentColor;
    border-radius: 9999px;
    background: transparent;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.ska-impersonation-bar__back:hover {
    background-color: rgba(0, 0, 0, 0.12);
}
