/* ===========================================================================
   Mundo visual: "Cinta de cotización".

   Una terminal de operador, no un panel SaaS. Tres decisiones lo sostienen:

   1. El color habla SOLO de mercado. Verde = long / bid / sano, rojo = short /
      ask / irreversible, ámbar = atención. La acción primaria del panel es
      hueso sobre gris: un botón nunca compite con una señal de precio.
   2. Todo número es monoespaciado con cifras tabulares (JetBrains Mono):
      precios, bps, nocionales, tiempos. Las etiquetas van en Inter Tight.
   3. La regla de precio (.ruler) es la firma: el eje que el bot cotiza,
      dibujado con sus capas, en la lista y en el login.
   --------------------------------------------------------------------------- */
:root {
    --bg: #0b0c10;
    --surface: #13141a;
    --surface-2: #191b22;
    --border: #252833;
    --border-strong: #363a48;
    --text: #e9e7e0;
    --text-muted: #9a9ca8;
    --text-faint: #6f7280;

    /* Semántica de mercado */
    --long: #31c76a;
    --long-soft: rgba(49, 199, 106, 0.13);
    --short: #ef5350;
    --short-soft: rgba(239, 83, 80, 0.13);
    --warn: #f0a93a;
    --warn-soft: rgba(240, 169, 58, 0.14);

    /* Acción del panel: hueso, sin color de mercado */
    --primary: #ece8dc;
    --primary-hover: #fbf8ef;
    --primary-ink: #14151a;

    /* Compatibilidad con reglas antiguas: mismos significados, nuevos valores. */
    --accent: var(--long);
    --accent-hover: #3dd977;
    --accent-soft: var(--long-soft);
    --danger: var(--short);
    --danger-on-tint: #f78784;
    --warning: var(--warn);

    --input-bg: #1b1c24;
    --disabled-bg: #131419;
    --placeholder: #6b6d78;
    --hover: rgba(255, 255, 255, 0.05);
    --hover-strong: rgba(255, 255, 255, 0.09);
    --scroll: #33364a;
    --scroll-hover: #454962;
    --backdrop: rgba(6, 7, 10, 0.72);

    --font-ui: "Inter Tight", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --font-num: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

    --radius: 6px;
    --radius-lg: 10px;
    --shadow-plate: 0 1px 2px rgba(0, 0, 0, 0.5), 0 16px 40px -12px rgba(0, 0, 0, 0.75);
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* --- Tema claro -------------------------------------------------------------
   Mismos significados, otra luz: el color de mercado (verde/rojo/ambar) se
   oscurece un punto para seguir legible sobre blanco. Se activa con
   data-theme="light" en <html> (boton del nav, guardado en localStorage). */
:root { color-scheme: dark; }
:root[data-theme="light"] {
    color-scheme: light;
    --bg: #f3f2ee;
    --surface: #ffffff;
    --surface-2: #f6f5f1;
    --border: #dedcd5;
    --border-strong: #c4c1b8;
    --text: #17181c;
    --text-muted: #5c5f6a;
    --text-faint: #8a8d97;
    --long: #1f9a4f;
    --long-soft: rgba(31, 154, 79, 0.12);
    --short: #d13b38;
    --short-soft: rgba(209, 59, 56, 0.12);
    --warn: #b9770e;
    --warn-soft: rgba(185, 119, 14, 0.14);
    --primary: #17181c;
    --primary-hover: #2b2d34;
    --primary-ink: #f6f5f1;
    --accent-hover: #178a45;
    --danger-on-tint: #b8322f;
    --input-bg: #ffffff;
    --disabled-bg: #efeee9;
    --placeholder: #9a9ca6;
    --hover: rgba(0, 0, 0, 0.04);
    --hover-strong: rgba(0, 0, 0, 0.08);
    --scroll: #c4c1b8;
    --scroll-hover: #a9a69d;
    --backdrop: rgba(30, 32, 40, 0.45);
    --shadow-plate: 0 1px 2px rgba(0, 0, 0, 0.08), 0 16px 40px -12px rgba(0, 0, 0, 0.25);
}
/* Móvil en oscuro: gris oscuro en vez de negro (en OLED el negro puro deja
   los bordes de las tarjetas casi invisibles). Escritorio sigue en negro. */
@media (max-width: 760px) {
    :root:not([data-theme="light"]) {
        --bg: #15161b;
        --surface: #1c1d24;
        --surface-2: #22242c;
        --border: #2d3039;
        --input-bg: #23252d;
    }
}
:root[data-theme="light"] .bot-face { fill: #2a2c34; }
:root[data-theme="light"] .auth-aura { background: radial-gradient(50% 50% at 50% 50%, rgba(23, 24, 28, 0.06), rgba(23, 24, 28, 0) 68%); }
:root[data-theme="light"] .error-row { color: var(--text); }

* { box-sizing: border-box; }

/* Utilidad para contenido solo-lector-de-pantalla (visualmente oculto). */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Enlace "saltar al contenido" — oculto hasta recibir foco de teclado. */
.skip-link {
    position: absolute;
    top: -40px;
    left: 8px;
    z-index: 100;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--accent);
    border-radius: 6px;
    padding: 8px 14px;
    text-decoration: none;
}
.skip-link:focus {
    top: 8px;
}

body {
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-ui);
    margin: 0;
    font-size: 13.5px;
    line-height: 1.45;
    -webkit-font-smoothing: antialiased;
}
/* Todo número comparable va en mono tabular: se barre en columna, no se lee
   uno por uno. .num ya existía como "cifras tabulares"; ahora también cambia
   la fuente. */
.num, .mono, .unit, .cell-sub, code, .kv dd.mono, .logs-table td:first-child,
.readonly-value .field-value, .grid-preview-price #grid-preview-price-value,
.grid-preview-avg, .filter-count, .status-stamp, input[type="number"] {
    font-family: var(--font-num);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
}
.unit { color: var(--text-faint); font-size: 0.85em; margin-left: 4px; }

.topnav {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 14px 24px;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
    flex-wrap: wrap;
}
/* Marca a la izquierda, enlaces a la derecha: centrar la marca obligaba a
   repartir el ancho en tres y a 1280 px los enlaces ya no cabían en una línea. */
.topnav-side:first-child { display: none; }
.topnav-brand { margin-right: auto; }
.topnav-side { display: flex; align-items: center; min-width: 0; }
.topnav-side.topnav-right { justify-content: flex-end; gap: 16px; flex: 0 1 auto; }
.topnav-links { display: flex; flex-wrap: nowrap; }
.topnav-brand {
    font-weight: 700;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    white-space: nowrap;
    color: var(--text);
}
.topnav-links a { font-weight: 500; font-size: 13px; }
.topnav-links .nav-link { display: inline-flex; align-items: center; gap: 7px; }
.topnav-links .nav-link svg { flex: none; }
.topnav-links a { color: var(--text-muted); text-decoration: none; margin-right: 4px; padding: 6px 12px; border-radius: 6px; transition: background 0.15s ease, color 0.15s ease; }
.topnav-links a:hover { color: var(--text); background: var(--hover); }
.topnav-links a.active { color: var(--text); background: var(--hover-strong); }

/* 1200, not 960: the bots table now carries ten columns and needs ~1055px,
   so at 960 the action buttons fell outside the card entirely. The forms
   sit in flex columns with their own min-width, so they just breathe a
   little more here rather than stretching awkwardly. */
.content { max-width: 1200px; margin: 0 auto; padding: 32px 24px; }
/* Las listas de bots traen 8 columnas más tres botones: a 1200 px salía
   una barra de desplazamiento aun con pantalla de sobra. Solo ellas se
   ensanchan; los formularios conservan su medida de lectura. */
.content:has(.ops-table) { max-width: 1560px; }
/* Cuenta y Administración son formularios de dos o tres campos: a ancho
   completo cada input mide más de un metro de pantalla. Mitad del ancho. */
.content:has(form[action="/settings/save"]) { max-width: 400px; }
/* Cuenta: perfil + 2FA + contraseña; mas aire que un formulario de dos campos. */
.content:has(form[action="/account/change-password"]) { max-width: 640px; }
/* Usuarios tiene tablas (usuarios, invitaciones): ancho de lista, no de formulario. */
.content:has(form[action="/usuarios/integrator/save"]) { max-width: 960px; }
.data-table td.actions { display: flex; gap: 6px; flex-wrap: wrap; }

.centered-card { display: flex; justify-content: center; margin-top: 80px; }
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 32px; width: 360px; }

h1 { font-size: 20px; margin: 0 0 16px; letter-spacing: -0.01em; }
.muted { color: var(--text-muted); }
.hint { color: var(--text-muted); font-size: 13px; margin: 4px 0 12px; }
.hint-inline { color: var(--text-muted); font-weight: normal; font-size: 12px; }
/* Dentro de una etiqueta la aclaración va debajo, no en la misma línea: una
   etiqueta de dos renglones se lee mejor que una de 90 caracteres. */
label > .hint-inline { display: block; margin-top: 2px; line-height: 1.35; }

.page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }

label { display: block; margin: 22px 0 6px; font-size: 13px; color: var(--text-muted); }
/* La primera etiqueta de una ficha va pegada al título: el margen de 22 px
   se leía como una fila vacía (visto en Telegram → Nivel mínimo). */
fieldset > label:first-of-type:first-child, fieldset > legend + label { margin-top: 4px; }
input, select {
    width: 100%;
    background: var(--input-bg);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 9px 10px;
    color: var(--text);
    font-size: 14px;
}
input:focus, select:focus { border-color: var(--accent); }

/* Indicador de foco visible para navegación por teclado (WCAG 2.4.7 / 2.4.11).
   Se usa :focus-visible para no mostrar el anillo en clics de mouse, pero sí
   siempre que se navegue con Tab. Contraste de --accent sobre --bg y --surface
   es amplio (>8:1), cumpliendo el 3:1 mínimo para indicadores no textuales. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 4px;
}
.checkbox-label { display: flex; align-items: center; gap: 8px; }
.checkbox-label input { width: auto; }

fieldset { border: 1px solid var(--border); border-radius: 8px; padding: 16px 20px; margin-bottom: 16px; }
legend { padding: 0 8px; color: var(--text-muted); font-size: 13px; text-transform: uppercase; letter-spacing: 0.04em; }

.form-columns { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 24px; }
.form-columns fieldset { flex: 1; min-width: 260px; margin-bottom: 0; }
.form-columns + fieldset { margin-top: 16px; }
details { margin-top: 8px; }
details summary { cursor: pointer; color: var(--text-muted); font-size: 13px; }

.form-actions { display: flex; gap: 12px; margin-top: 20px; }
/* Un formulario seguido de otra ficha (Settings: Guardar y luego Invitaciones)
   necesita aire debajo del boton, no solo encima. */
form.form + fieldset, form.form + form { margin-top: 32px; }
#integ-msg.is-danger { color: var(--short); }

.btn-primary, .btn-secondary {
    display: inline-block;
    padding: 9px 16px;
    border-radius: 6px;
    text-decoration: none;
    font-size: 14px;
    border: none;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.btn-primary { background: var(--primary); color: var(--primary-ink); font-weight: 600; }
.btn-primary:hover { background: var(--primary-hover); }
.btn-primary:active { transform: scale(0.98); }
.btn-primary.is-warning { background: var(--warn); color: #1a1204; }
.btn-primary.is-danger { background: var(--short); color: #fff; }
.btn-secondary { background: transparent; border: 1px solid var(--border); color: var(--text); }
.btn-secondary:hover { border-color: var(--text-muted); }

.link-button {
    background: none; border: none; color: var(--text-muted); cursor: pointer;
    font-size: 14px; text-decoration: underline; transition: color 0.15s ease;
    /* Padding + margen negativo: agranda el área de toque a >=24px de alto
       (WCAG 2.5.8 Target Size) sin desplazar el layout visual (el margen
       negativo compensa el padding agregado). */
    display: inline-block;
    padding: 6px 2px;
    margin: -6px -2px;
}
.link-button:hover { color: var(--text); }
.link-button.danger { color: var(--danger); }
.topnav-logout { margin: 0; }

.icon-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 6px;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.icon-button:hover { color: var(--text); background: var(--hover); border-color: var(--border); }
/* Página actual en los iconos del nav: mismo fondo que el enlace activo. */
.icon-button.active { color: var(--text); background: var(--hover-strong); border-color: var(--border); }
/* Botón de tema: muestra el icono del tema al que cambiarías. */
.theme-toggle .icon-moon { display: none; }
:root[data-theme="light"] .theme-toggle .icon-sun { display: none; }
:root[data-theme="light"] .theme-toggle .icon-moon { display: block; }
.theme-toggle-floating { position: fixed; top: 14px; right: 16px; z-index: 5; }

/* overflow-x is auto, not hidden: `hidden` clipped the Iniciar/Detener/
   Eliminar buttons out of sight once the bots table grew extra columns —
   silently, with no scrollbar to hint anything was cut off. The rounded
   corners still clip correctly with auto. */
.table-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; overflow-x: auto; }
.detail-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 20px 24px; margin-bottom: 20px; }
.detail-card p { margin: 0 0 10px; }
.detail-card p:last-child { margin-bottom: 0; }
.data-table { width: 100%; border-collapse: collapse; }
.table-card .data-table { margin: 0; }
.data-table th, .data-table td { text-align: left; padding: 12px 16px; border-bottom: 1px solid var(--border); }
.data-table th { color: var(--text-muted); font-weight: 500; font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; background: rgba(255,255,255,0.02); }
.data-table tbody tr { transition: background 0.1s ease; }
.data-table tbody tr:hover { background: var(--hover); }
.data-table tbody tr:last-child td { border-bottom: none; }
.row-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.row-actions a {
    color: var(--text-muted); text-decoration: none; transition: color 0.15s ease;
    display: inline-block;
    padding: 6px 2px;
    margin: -6px -2px;
}
.row-actions a:hover { color: var(--text); }
.row-actions form { display: inline; }

.btn-action {
    display: inline-block;
    box-sizing: border-box;
    min-width: 80px;
    text-align: center;
    padding: 6px 12px;
    margin: 0;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text);
    font-family: inherit;
    font-size: 12px;
    line-height: 1.4;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.btn-action:hover { background: var(--hover); border-color: var(--text-muted); color: var(--text); }
.btn-action.danger { color: var(--danger-on-tint); border-color: rgba(239,68,68,0.35); }
.btn-action.danger:hover { background: rgba(239,68,68,0.08); border-color: var(--danger); }
.btn-action.btn-danger-solid { background: var(--short); border-color: var(--short); color: #fff; }
.btn-action.btn-danger-solid:hover { background: #d94643; border-color: #d94643; }

/* .row-actions a's own color/padding rule above has higher specificity
   than a bare .btn-action — this extra selector wins back the button look
   for the Editar <a> inside .row-actions specifically. */
.row-actions a.btn-action { color: var(--text); padding: 6px 12px; margin: 0; }
.row-actions a.btn-action:hover { color: var(--text); }

/* --- Tablas de operación (bots futuros, spot, exchanges) ------------------
   Texto a la izquierda, números a la derecha, filas de 40 px. Antes todo iba
   centrado: los decimales no alineaban y una columna de nocionales no se
   podía barrer. */
.ops-table th, .ops-table td { padding: 9px 10px; font-size: 13px; vertical-align: middle; }
.ops-table th { font-size: 11px; letter-spacing: 0.05em; text-align: center; }
.ops-table td.num { text-align: right; white-space: nowrap; }
.ops-table .status-pill { white-space: nowrap; font-size: 12px; }
.ops-table .row-actions { flex-wrap: nowrap; gap: 6px; justify-content: flex-end; }
.ops-table .btn-action,
.ops-table .row-actions a.btn-action { padding: 6px 12px; font-size: 12.5px; white-space: nowrap; }
.ops-table .cell-id { min-width: 150px; }
.ops-table .cell-name { display: block; font-weight: 600; letter-spacing: -0.01em; }
.ops-table .cell-sub {
    display: block; margin-top: 2px; font-size: 11px; color: var(--text-muted);
    white-space: nowrap; font-family: var(--font-num);
}
.ops-table .cell-sub .exchange-logo { width: 12px; height: 12px; vertical-align: -2px; margin-right: 5px; }
.ops-table td.num .cell-sub { text-align: right; }
.ops-table td.is-adjusted { color: var(--warn); }
.ops-table .is-danger { color: var(--danger-on-tint); }
.ops-table .direction { font-weight: 600; font-size: 12px; letter-spacing: 0.02em; }
.ops-table .cell-ruler { min-width: 300px; }
/* Símbolo, Mid y Estado se ciñen a su contenido: el espacio sobrante va a
   la regla de precio, que es la columna que se lee. */
.ops-table td.col-symbol, .ops-table td.col-mid, .ops-table td.col-avg, .ops-table td.col-status { width: 1%; white-space: nowrap; }
/* La píldora de estado va centrada bajo su encabezado (que ya lo está). */
.ops-table td.col-status, .ops-table td:has(> .status-pill[data-role]),
.ops-table td:has(> .direction) { text-align: center; }
.ops-table .cell-ruler .cell-sub { margin-top: 1px; }

/* --- Regla de precio -------------------------------------------------------
   Eje horizontal en bps alrededor del mid. Ticks de compra a la izquierda
   (verde), de venta a la derecha (rojo); el lado que solo puede reducir
   posición (dirección long/short) va hueco. Zona sombreada = banda no-trade. */
.ruler { display: block; overflow: hidden; }
.ruler-axis { stroke: var(--border-strong); stroke-width: 1; }
.ruler-mid { stroke: var(--text-faint); stroke-width: 1; }
.ruler-mid.is-live { stroke: var(--text); stroke-width: 1.5; }
.ruler-bid { stroke: var(--long); stroke-width: 2; stroke-linecap: round; }
.ruler-ask { stroke: var(--short); stroke-width: 2; stroke-linecap: round; }
.ruler-bid.is-exit, .ruler-ask.is-exit { stroke-width: 1; opacity: 0.55; stroke-dasharray: 2 2; }
.ruler-dead { fill: var(--warn); opacity: 0.16; }

/* --- Sello de estado en vivo ---------------------------------------------- */
.status-stamp { font-size: 11px; color: var(--text-faint); }
.status-stamp.is-stale { color: var(--warn); }

/* --- Diálogo de confirmación ---------------------------------------------- */
.confirm-dialog {
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    background: var(--surface);
    color: var(--text);
    padding: 0;
    width: min(440px, calc(100vw - 32px));
    box-shadow: var(--shadow-plate);
}
.confirm-dialog::backdrop { background: var(--backdrop); backdrop-filter: blur(2px); }
.confirm-inner { padding: 22px 24px 20px; }
.confirm-title { margin: 0 0 8px; font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.confirm-body { margin: 0; color: var(--text-muted); font-size: 13px; line-height: 1.5; }
.confirm-body[hidden] { display: none; }
.confirm-facts {
    display: grid; grid-template-columns: auto 1fr; gap: 6px 16px;
    margin: 14px 0 0; padding: 12px 14px;
    background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius);
    font-size: 12.5px;
}
.confirm-facts[hidden] { display: none; }
.confirm-facts dt { color: var(--text-muted); }
.confirm-facts dd { margin: 0; font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.confirm-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }
@keyframes confirm-in { from { opacity: 0; transform: translateY(8px) scale(0.985); } to { opacity: 1; transform: none; } }
.confirm-dialog[open] { animation: confirm-in 0.28s var(--ease-out); }
@media (prefers-reduced-motion: reduce) { .confirm-dialog[open] { animation: none; } }

/* --- Verificación en dos pasos ------------------------------------------- */
.twofa-enroll { display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap; }
.twofa-qr svg { display: block; border-radius: var(--radius); }
.twofa-secret { margin: 4px 0; font-size: 15px; letter-spacing: 0.06em; user-select: all; }
.backup-codes { list-style: none; margin: 8px 0 0; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 24px; max-width: 360px; }
.backup-codes li { font-size: 15px; letter-spacing: 0.04em; user-select: all; }
.content:has(form[action="/account/2fa/confirm"]) { max-width: 560px; }

/* --- Búsqueda en actividad ------------------------------------------------ */
.filter-search {
    width: 220px; padding: 6px 10px; font-size: 13px; border-radius: 999px;
    background: var(--surface); margin-right: 6px;
}
/* Actividad en móvil: los chips ocupan tres filas; en su lugar, la búsqueda
   y dos desplegables (nivel, bot). En escritorio es al revés. */
.filter-bar-compact { display: none !important; }
.filter-select { width: auto; flex: 1 1 0; min-width: 0; padding: 7px 10px; font-size: 13px; border-radius: 999px; background: var(--surface); }
@media (max-width: 760px) {
    #log-filter, #bot-filter { display: none !important; }
    .filter-bar-compact:not([hidden]) { display: flex !important; }
    /* Con las filas en grid, la <table> seguía calculando su ancho como
       tabla (columnas fantasma a la derecha y scroll vacío). En móvil la
       tabla y el tbody son bloques al ancho de la tarjeta. */
    .logs-table, .logs-table tbody,
    .ops-table.is-futuros, .ops-table.is-futuros tbody,
    .ops-table.is-spot, .ops-table.is-spot tbody,
    .ops-table.is-exchanges, .ops-table.is-exchanges tbody { display: block; width: 100%; }
    .table-card { overflow-x: hidden; }
    /* Cada evento en dos filas: hora · bot · nivel · categoría / mensaje. */
    .logs-table thead { display: none; }
    .logs-table tbody tr {
        display: grid;
        grid-template-columns: auto 1fr auto auto;
        grid-template-areas:
            "when bot level cat"
            "msg msg msg msg";
        gap: 4px 10px;
        align-items: center;
        padding: 10px 14px;
        border-bottom: 1px solid var(--border);
    }
    .logs-table tbody tr:last-child { border-bottom: none; }
    .logs-table tbody td { display: block; padding: 0; border: 0; }
    .logs-table tbody td:nth-child(1) { grid-area: when; font-size: 11px; }
    .logs-table tbody td:nth-child(2) { grid-area: bot; font-size: 11px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .logs-table tbody td:nth-child(3) { grid-area: level; }
    .logs-table tbody td:nth-child(4) { grid-area: cat; }
    .logs-table tbody td:nth-child(5) { grid-area: msg; font-size: 12.5px; line-height: 1.45; }
    .logs-card { max-height: none; }
}
.form-notice.is-ok {
    background: var(--long-soft); border-color: rgba(49, 199, 106, 0.3); color: var(--text);
}

.badge { background: var(--input-bg); border: 1px solid var(--border); border-radius: 4px; padding: 2px 8px; font-size: 12px; }
.badge-muted { color: var(--text-muted); margin-left: 6px; }

.status-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
.status-running { background: var(--accent); }
.status-stopped { background: var(--text-muted); }
.status-error { background: var(--danger); }

.status-pill { display: inline-flex; align-items: center; padding: 3px 10px 3px 8px; border-radius: 999px; font-size: 12px; font-weight: 500; }
.status-pill .status-dot { margin-right: 6px; }
.status-pill-running { background: var(--long-soft); color: var(--long); }
.status-pill-stopped { background: rgba(139,141,152,0.12); color: var(--text-muted); }
.status-pill-error { background: var(--short-soft); color: var(--danger-on-tint); }
.status-pill-info { background: rgba(139,141,152,0.12); color: var(--text-muted); }
.status-pill-warning { background: var(--warn-soft); color: var(--warn); }
.status-warning { background: var(--warn); }

.exchange-logo { width: 15px; height: 15px; border-radius: 50%; object-fit: contain; background: #fff; padding: 1px; }

.direction-long { color: var(--accent); font-weight: 700; }
.direction-short { color: var(--danger); font-weight: 700; }
.direction-neutral { color: var(--text-muted); }

.error { color: var(--danger-on-tint); font-size: 13px; }
/* La fila de error pertenece a la fila del bot de arriba: sin borde entre las
   dos, fondo teñido, texto legible (no rojo sobre granate). */
.error-row {
    color: var(--text); font-size: 12px; line-height: 1.5;
    background: var(--short-soft); font-family: var(--font-num);
    border-left: 2px solid var(--short) !important; /* 2px: marca, no banda */
    padding-left: 14px !important;
}
.data-table tbody tr:has(+ .error-row-tr) td { border-bottom: none; }

/* Number inputs: no spinner arrows anywhere — every numeric field here is
   typed, never stepped, and the arrows only crowd the field and invite
   accidental single-unit changes to trading parameters. Keyboard arrows and
   form validation are unaffected; this is purely the visual control. */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

/* ---------------------------------------------------------------------------
   Compact multi-column forms (bot edit, connection edit).
   Scoped to .form-columns on purpose: the global label/input rules above set
   the comfortable spacing short forms like login want, and those should keep
   it. The bot form is ~30 fields across 8 fieldsets, where that same spacing
   turns into a lot of scrolling.
   --------------------------------------------------------------------------- */
.form-columns { gap: 12px; margin-bottom: 12px; }
.form-columns fieldset { padding: 10px 14px 12px; }
.form-columns legend { font-size: 12px; }
.form-columns label { margin: 10px 0 3px; font-size: 12px; line-height: 1.3; }
/* The first control already sits right under the legend. */
.form-columns fieldset > label:first-of-type { margin-top: 2px; }
.form-columns input,
.form-columns select { padding: 6px 9px; font-size: 13px; border-radius: 5px; }
.form-columns .hint { font-size: 12px; line-height: 1.35; margin: 2px 0 8px; }
.form-columns .hint-inline { font-size: 11px; line-height: 1.3; display: block; margin-top: 3px; }
.form-columns .muted { font-size: 12px; margin: 2px 0; }
.form-columns details { margin-top: 6px; }
.form-columns details summary { font-size: 12px; }
/* Preview table inside the notional-multiplier row. */
.form-columns .data-table th,
.form-columns .data-table td { padding: 6px 8px; font-size: 12px; }
.form-columns .data-table th { font-size: 11px; }



/* ===========================================================================
   Pantallas sin sesión: login, recuperar y restablecer contraseña.

   Estas tres son las únicas que usan .auth-* y eran las únicas que usaban
   .card / .centered-card, así que el cascarón se rehízo completo sin tocar
   ninguna pantalla autenticada.

   La composición es una sola placa centrada sobre el fondo casi negro, con
   la marca apoyada en una línea que cruza el ancho — el eje de precio que
   el bot cotiza. Es la misma marca del favicon y el mismo tratamiento en
   versalitas espaciadas del .topnav-brand, para que entrar y estar dentro
   se sientan el mismo producto.
   --------------------------------------------------------------------------- */

/* --- Superficies que dibuja el navegador, no nosotros ---------------------
   Selección, cursor de texto y autocompletado vienen con estilos de fábrica
   que no pertenecen a ningún sistema de diseño. En una pantalla oscura el
   autofill de Chrome es el peor: pinta el campo de blanco y deja el texto
   ilegible. Se tiñen desde la paleta. */
::selection { background: var(--accent-soft); color: var(--text); }
body { caret-color: var(--accent); }

input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
    /* box-shadow interior en vez de background: es lo único que Chrome no
       sobrescribe en el estado de autocompletado. */
    -webkit-box-shadow: 0 0 0 1000px var(--input-bg) inset;
    -webkit-text-fill-color: var(--text);
    caret-color: var(--accent);
}

.body-auth {
    /* El fondo se extiende más allá del contenido cuando la placa es corta. */
    min-height: 100vh;
}
.body-auth .content { max-width: none; margin: 0; padding: 0; }

.auth {
    position: relative;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 40px 20px 56px;
    overflow: hidden;
}

/* Luz ambiental detrás de la marca: la placa no flota en un vacío plano.
   Muy baja opacidad — se nota si se quita, no si está. */
.auth-aura {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 620px;
    max-width: 140vw;
    height: 420px;
    /* Anclada al bloque de contenido, no al borde superior: centrada cerca
       de la marca, para que se lea como luz emitida por ella y no como una
       mancha suelta arriba de la pantalla. */
    transform: translate(-50%, -88%);
    background: radial-gradient(50% 50% at 50% 50%, rgba(236, 232, 220, 0.05), rgba(236, 232, 220, 0) 68%);
    pointer-events: none;
}

.auth-inner {
    position: relative;
    width: 100%;
    max-width: 400px;
    /* Centro óptico por encima del geométrico: una placa centrada a 50%
       se lee caída. */
    margin-bottom: 4vh;
}

/* --- Marca ---------------------------------------------------------------- */
.auth-lockup {
    display: flex;
    align-items: center;
    gap: 14px;
    /* Más arriba: el bot ocupa el espacio entre la marca y la placa. */
    margin-bottom: 8px;
}
.auth-rule {
    flex: 1;
    height: 1px;
    min-width: 12px;
    background: linear-gradient(to right, rgba(38, 40, 50, 0), var(--border));
}
.auth-rule:last-child {
    background: linear-gradient(to left, rgba(38, 40, 50, 0), var(--border));
}
.auth-mark { display: block; flex: none; }
.auth-wordmark {
    flex: none;
    margin: 0;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--text);
    white-space: nowrap;
}

/* --- Placa ---------------------------------------------------------------- */
.auth-plate {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 28px 28px 30px;
    box-shadow: var(--shadow-plate);
}
.auth-plate h1 {
    font-size: 16px;
    font-weight: 600;
    margin: 0;
    letter-spacing: -0.01em;
    text-align: center;
}
/* El texto de apoyo pertenece al título, no al campo que sigue: pegado
   arriba, separado abajo. */
.auth-lede {
    color: var(--text-muted);
    font-size: 13px;
    line-height: 1.5;
    margin: 8px 0 0;
}
.auth-form { margin-top: 24px; }
.auth-form label {
    margin: 0 0 7px;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: var(--text-muted);
}
.auth-field + .auth-field { margin-top: 18px; }
.auth-field input {
    padding: 11px 12px;
    border-radius: 8px;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.auth-field input::placeholder { color: var(--placeholder); }
.auth-field input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Campo de contraseña con botón de ver/ocultar dentro del marco. */
.pw-field { position: relative; }
.pw-field input { padding-right: 44px; }
.pw-toggle {
    position: absolute;
    right: 4px;
    bottom: 4px;
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    background: none;
    border: none;
    border-radius: 6px;
    color: var(--text-muted);
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
}
.pw-toggle:hover { color: var(--text); background: var(--hover); }
.pw-toggle svg { display: block; }

/* Aviso de mayúsculas: sólo aparece con Bloq Mayús activo. Es funcional —
   es la causa más común de una contraseña correcta rechazada. */
.pw-caps {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 7px;
    font-size: 12px;
    color: var(--warning);
}
.pw-caps[hidden] { display: none; }

/* --- Requisitos en vivo (primer arranque y restablecer) ------------------- */
.auth-reqs {
    list-style: none;
    margin: 14px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.auth-reqs li {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: var(--text-muted);
    transition: color 0.15s ease;
}
.auth-reqs li.met { color: var(--accent); }
.auth-reqs .req-dot {
    flex: none;
    width: 12px;
    height: 12px;
    /* Anillo apagado, no currentColor: a tamano de radio y con el color del
       texto, un circulo hueco se lee como control seleccionable. */
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: 50%;
    position: relative;
}
/* La palomita se dibuja con dos bordes rotados: sin glifo Unicode haciendo
   de icono, y sin cargar un SVG por viñeta. */
.auth-reqs li.met .req-dot { background: var(--accent); border-color: var(--accent); }
.auth-reqs li.met .req-dot::after {
    content: "";
    position: absolute;
    left: 3.5px;
    top: 1px;
    width: 3px;
    height: 5.5px;
    border: solid #06110a;
    border-width: 0 1.5px 1.5px 0;
    transform: rotate(45deg);
}

/* --- Error ---------------------------------------------------------------- */
.auth-error {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 0 0 20px;
    padding: 11px 13px;
    background: rgba(239, 68, 68, 0.09);
    border: 1px solid rgba(239, 68, 68, 0.3);
    border-radius: 8px;
    color: var(--danger-on-tint);
    font-size: 13px;
    line-height: 1.45;
}
.auth-error svg { flex: none; margin-top: 1px; }

/* --- Acciones ------------------------------------------------------------- */
.auth-submit {
    width: 100%;
    margin-top: 22px;
    padding: 11px 16px;
    border-radius: 8px;
    font-size: 14px;
}
.auth-submit[aria-busy="true"] { opacity: 0.7; cursor: progress; }
.auth-actions { display: flex; gap: 10px; margin-top: 22px; }
.auth-actions .btn-secondary,
.auth-actions .btn-primary { flex: 1; text-align: center; padding: 11px 16px; border-radius: 8px; }

.auth-foot { margin: 18px 0 0; text-align: center; font-size: 13px; }
@media (max-width: 760px) {
    .auth-foot-sep { display: none; }
    .auth-foot a { display: block; margin: 8px auto 0; width: fit-content; }
}
.auth-foot a {
    color: var(--text-muted);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    padding-bottom: 1px;
    transition: color 0.15s ease, border-color 0.15s ease;
}
.auth-foot a:hover { color: var(--text); border-bottom-color: var(--border); }

/* --- El bot de la puerta ---------------------------------------------------
   Cabeza en perspectiva (rotateX/Y desde auth.js), manos que suben a tapar
   los ojos al escribir la contraseña y "asoman" de vez en cuando. Todo son
   transforms: nada re-maqueta la placa. */
.auth-bot {
    display: flex;
    justify-content: center;
    /* Medio cuerpo: la placa (z-index 1) tapa el torso y las manos en
       reposo, así que el bot se lee asomado por detrás de ella. */
    margin: 0 0 -62px;
    position: relative;
    z-index: 0;
    perspective: 420px;
}
.auth-plate { position: relative; z-index: 1; }
.auth-bot-svg { overflow: visible; }
.bot-head {
    transform-origin: 70px 56px;
    transition: transform 0.25s var(--ease-out);
    transform-style: preserve-3d;
}
.bot-neck { fill: var(--bot-metal-dark); }
/* Plata: el gris de las superficies se perdía contra el fondo. */
.auth-bot { --bot-metal: #b9bcc6; --bot-metal-edge: #e3e5ea; --bot-metal-dark: #8e919c; }
.bot-stroke { stroke: var(--bot-metal-dark); stroke-width: 2; }
.bot-antenna { fill: var(--long); }
.bot-shell { fill: var(--bot-metal); stroke: var(--bot-metal-edge); stroke-width: 1.5; }
.bot-face { fill: #1a1c24; stroke: var(--bot-metal-dark); stroke-width: 1; }
.bot-sclera { fill: var(--primary); }
.bot-pupil { fill: var(--bg); transition: transform 0.12s ease-out, r 0.2s ease; }
.bot-mouth { fill: none; stroke: var(--primary); stroke-width: 2; stroke-linecap: round; transition: d 0.3s ease; }
/* Tema claro: --primary es hueso y se pierde sobre la cara plateada. */
:root[data-theme="light"] .bot-mouth { stroke: #ffffff; stroke-width: 2.2; }
.bot-eye { transform-origin: center; transition: transform 0.25s var(--ease-out); }
.bot-limb { fill: var(--bot-metal-dark); stroke: var(--bot-metal-edge); stroke-width: 1.5; }
.bot-hand { fill: var(--bot-metal); stroke: var(--bot-metal-edge); stroke-width: 1.5; }
/* Cada brazo gira desde su hombro (coordenadas del viewBox). En reposo
   cuelga hacia abajo, tras la placa; al taparse rota ~165° y la mano cae
   justo sobre su ojo (hombro (40,100) → ojo (56,47); simétrico a la derecha).
   Espiando: el derecho baja unos grados y deja ver medio ojo. */
.bot-arm {
    transform-box: view-box;
    transition: transform 0.38s var(--ease-out);
}
.bot-arm-l { transform-origin: 40px 100px; transform: rotate(10deg); }
.bot-arm-r { transform-origin: 100px 100px; transform: rotate(-10deg); }
.auth-bot.is-covering .bot-arm-l { transform: rotate(-164deg); }
.auth-bot.is-covering .bot-arm-r { transform: rotate(164deg); }
.auth-bot.is-covering.is-peeking .bot-arm-r { transform: rotate(140deg); }
/* Los brazos suben desde el torso (oculto tras la placa) hasta los ojos, que
   ya asoman por encima: no hace falta traer el bot al frente, y hacerlo
   pintaba el torso sobre el título de la placa. */
.auth-bot.is-covering.is-peeking .bot-eye-r { transform: scaleY(0.55); }
/* Contraseña visible: manos abajo y ojos como platos. */
.auth-bot.is-shocked .bot-eye { transform: scale(1.25); }
.auth-bot.is-shocked .bot-pupil { r: 2.4; }
.auth-bot.is-shocked .bot-mouth { d: path("M64 60 Q70 68 76 60"); }
@media (prefers-reduced-motion: reduce) {
    .bot-head, .bot-arm, .bot-eye, .bot-pupil { transition: none; }
}

/* --- Un solo momento de entrada ------------------------------------------
   La línea se abre desde la marca hacia los bordes, el trazo del gráfico se
   dibuja solo, y la placa se asienta. Ocurre una vez al cargar; no hay nada
   animado después. */
@keyframes auth-rule-in { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes auth-plate-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes auth-draw { from { stroke-dashoffset: 40; } to { stroke-dashoffset: 0; } }
@keyframes auth-fade-in { from { opacity: 0; } to { opacity: 1; } }

.auth-rule { transform-origin: right center; animation: auth-rule-in 0.7s var(--ease-out) both; }
.auth-rule:last-child { transform-origin: left center; }
.auth-wordmark { animation: auth-fade-in 0.6s var(--ease-out) 0.15s both; }
.auth-mark { color: var(--text); }
.auth-mark .mark-line { stroke-dasharray: 40; stroke-dashoffset: 40; animation: auth-draw 0.8s var(--ease-out) 0.2s both; }
.auth-mark .mark-ring { animation: auth-fade-in 0.6s var(--ease-out) both; }
.auth-plate { animation: auth-plate-in 0.6s var(--ease-out) 0.12s both; }
.auth-foot { animation: auth-fade-in 0.6s var(--ease-out) 0.35s both; }

@media (prefers-reduced-motion: reduce) {
    .auth-rule, .auth-wordmark, .auth-mark .mark-line,
    .auth-mark .mark-ring, .auth-plate, .auth-foot { animation: none; }
    .auth-mark .mark-line { stroke-dasharray: none; }
}

@media (max-width: 420px) {
    .auth { padding: 28px 16px 40px; }
    .auth-plate { padding: 22px 20px 24px; }
    .auth-plate h1 { font-size: 20px; }
    .auth-inner { margin-bottom: 0; }
}

/* Alto reducido (laptop corta, navegador con muchas barras): el centrado
   vertical deja de tener sentido y la placa debe poder desplazarse. */
@media (max-height: 640px) {
    .auth { justify-content: flex-start; padding-top: 32px; }
    .auth-inner { margin-bottom: 0; }
}


/* ===========================================================================
   Pantallas de operacion (Bots, Exchanges, Logs, Telegram).

   Modo operativo: la meta es completar una tarea, no admirar la pantalla.
   Todo lo de aqui es densidad legible y jerarquia correcta; la marca vive en
   los detalles, no en adornos que compitan con los datos.
   --------------------------------------------------------------------------- */

/* --- Barra de scroll -------------------------------------------------------
   .table-card desplaza horizontalmente cuando la tabla de bots no cabe, asi
   que su barra es parte de la interfaz y no puede seguir siendo la gris de
   fabrica sobre un tema oscuro. */
* { scrollbar-width: thin; scrollbar-color: var(--scroll) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--scroll); border-radius: 999px; }
::-webkit-scrollbar-thumb:hover { background: var(--scroll-hover); }

/* --- Cifras tabulares ------------------------------------------------------
   Sin esto, "15.00" y "7.50" no alinean sus puntos decimales y comparar una
   columna de numeros obliga a leerlos uno por uno en vez de barrerlos. */
.num, .data-table .num, .logs-table td:first-child {
    font-variant-numeric: tabular-nums;
}
.num { text-align: right; }

/* --- Encabezado de pagina -------------------------------------------------- */
.page-header { align-items: flex-start; }
.page-header h1 { margin: 0; }
.page-title-group { display: flex; flex-direction: column; gap: 5px; }
.page-lede { margin: 0; color: var(--text-muted); font-size: 13px; }
.page-lede .sep { opacity: 0.45; margin: 0 7px; }

/* --- Iconos dentro de pildoras de estado ------------------------------------
   Reemplazan los emoji que habia antes: un glifo Unicode se dibuja distinto en
   cada sistema operativo y no comparte grosor de trazo con los iconos del nav. */
.status-pill svg { flex: none; margin-right: 6px; }

/* --- Estado vacio ----------------------------------------------------------
   Una linea gris diciendo "no hay nada" describe el problema y abandona al
   usuario ahi mismo. Estas pantallas empiezan vacias de verdad, asi que el
   estado vacio es la primera pantalla real del producto: dice que falta y
   lleva al siguiente paso. */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 54px 24px 58px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
}
.empty-state .empty-mark { color: var(--text-muted); opacity: 0.55; margin-bottom: 16px; }
.empty-state h2 { margin: 0 0 8px; font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.empty-state p {
    margin: 0 0 20px;
    color: var(--text-muted);
    font-size: 13px;
    line-height: 1.55;
    max-width: 44ch;
}
.empty-state p:last-child { margin-bottom: 0; }

/* --- Jerarquia de acciones de fila -----------------------------------------
   Estaba invertida: "Detener" (pausa reversible) era rojo solido y "Eliminar"
   (irreversible) era un contorno tenue. Detener conserva presencia con ambar
   —es una accion consecuente sobre dinero en vivo— y el rojo queda reservado
   para lo que no se puede deshacer. */
.btn-action.warning { color: var(--warn); border-color: rgba(240, 169, 58, 0.4); }
.btn-action.warning:hover { background: var(--warn-soft); border-color: var(--warn); }

/* --- Logs ------------------------------------------------------------------
   Hasta 200 filas. Con el encabezado fijo dentro de su propio contenedor, la
   columna que estas leyendo sigue teniendo nombre despues de la fila 30. */
.logs-card { max-height: calc(100vh - 230px); overflow: auto; }
.logs-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    /* Opaco a proposito: el rgba del encabezado normal dejaria ver las filas
       pasando por debajo. */
    background: var(--surface-2);
}
.logs-table td:first-child { white-space: nowrap; color: var(--text-muted); }
.logs-table td:last-child { line-height: 1.5; }

/* --- Pares clave/valor (Telegram) ------------------------------------------ */
.kv { display: grid; grid-template-columns: auto 1fr; gap: 10px 20px; margin: 0; }
.kv dt { color: var(--text-muted); font-size: 13px; }
.kv dd { margin: 0; font-size: 13px; }
.kv dd.mono {
    /* Monoespaciada aqui porque son valores que se comparan caracter por
       caracter contra el servidor, no por parecer tecnico. */
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12.5px;
}
.kv dd.unset { color: var(--text-muted); font-style: italic; }

@media (max-width: 560px) {
    .page-header { flex-direction: column; gap: 14px; }
    /* Actividad: el botón Vaciar se queda a la derecha del título, en la
       misma línea, en vez de bajar debajo. */
    .page-header.page-header-inline { flex-direction: row; justify-content: space-between; align-items: center; gap: 12px; }
    .kv { grid-template-columns: 1fr; gap: 3px 0; }
    .kv dd { margin-bottom: 10px; }
}

/* Nombres de archivo y valores literales dentro de texto corrido. */
code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.92em;
    background: var(--input-bg);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 1px 5px;
    color: var(--text);
}
.error-row svg { vertical-align: -2px; margin-right: 7px; }

/* --- Correcciones de la ronda de revision --------------------------------- */

.error-row { text-align: left; }

/* La nota quedaba pegada a la lista y se leia como una cuarta fila de ella. */
.kv-note { margin: 16px 0 0; padding-top: 14px; border-top: 1px solid var(--border); }

/* Tres opciones no necesitan todo el ancho del fieldset. */
.select-narrow { max-width: 320px; }

/* El nav se apilaba verticalmente en ventanas angostas porque los enlaces y
   los iconos competian por la misma fila. Bajo 760px el titulo pasa a su
   propia linea y los enlaces se reparten el ancho completo. */
@media (max-width: 760px) {
    .topnav { padding: 12px 16px; gap: 10px; }
    .topnav-brand { order: -1; flex: 1 1 100%; text-align: center; margin-right: 0; }
    .topnav-side.topnav-right { flex: 1 1 100%; justify-content: space-between; gap: 6px; }
    .topnav-links { display: flex; flex-wrap: nowrap; gap: 2px; }
    .topnav-links a { margin-right: 0; }
    /* Móvil: solo iconos, con el mismo tamaño de toque que los demás. */
    .topnav-links .nav-label { display: none; }
    .topnav-links .nav-link { width: 32px; height: 32px; padding: 0; justify-content: center; }
    .content { padding: 20px 16px; }
}

/* --- Filtro por nivel en /logs ---------------------------------------------
   Control segmentado. Comparte el radio y el grosor de borde de .btn-action
   para que se lea como parte del mismo sistema y no como un widget pegado. */
.filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 14px;
}
.filter-chip {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 6px 12px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: transparent;
    color: var(--text-muted);
    font-family: inherit;
    font-size: 13px;
    line-height: 1.4;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.filter-chip:hover { color: var(--text); border-color: var(--text-muted); }
.filter-chip.is-active {
    background: var(--hover-strong);
    border-color: var(--text-muted);
    color: var(--text);
}
.filter-count {
    /* Tabulares: el ancho del chip no baila al cambiar de 9 a 10. */
    font-variant-numeric: tabular-nums;
    font-size: 12px;
    color: var(--text-muted);
    background: var(--hover);
    border-radius: 999px;
    padding: 1px 6px;
    min-width: 20px;
    text-align: center;
}
.filter-chip.is-active .filter-count { color: var(--text); background: var(--hover-strong); }
/* Un nivel sin eventos sigue siendo pulsable: apagarlo del todo esconderia
   el dato de que ese nivel existe y esta en cero. */
.filter-chip.is-empty { opacity: 0.5; }
/* Al seleccionarlo recupera opacidad plena: si no, el chip activo es el mas
   apagado de la barra y se pierde cual esta aplicado. */
.filter-chip.is-empty.is-active { opacity: 1; }

.filter-empty {
    margin: 0;
    padding: 28px 16px;
    text-align: center;
    color: var(--text-muted);
    font-size: 13px;
}
.filter-empty[hidden] { display: none; }

/* ===========================================================================
   Ficha de edicion de bot.

   Es el formulario mas largo del panel y el unico que puede cambiar el
   comportamiento de dinero en vivo. El trabajo aqui es que se note que estas
   editando algo consecuente, sin volverlo mas lento de llenar.
   --------------------------------------------------------------------------- */

/* --- Campos bloqueados -----------------------------------------------------
   No habia NINGUNA regla :disabled. Con el bot corriendo, nombre, conexion,
   simbolo y direccion quedan bloqueados y se veian exactamente igual que los
   editables: descubrias que no se podia escribir al intentarlo. */
input:disabled,
select:disabled {
    background: var(--disabled-bg);
    border-color: var(--border);
    color: var(--text-muted);
    cursor: not-allowed;
}
/* La etiqueta acompana al campo: si el control esta apagado, su nombre
   tambien, para que el par completo se lea como bloqueado. */
.form-columns fieldset:has(input:disabled) label,
.form-columns fieldset:has(select:disabled) label { color: var(--placeholder); }

/* --- Orientacion dentro de un formulario de ocho fichas --------------------
   Un borde tenue marca en cual estas escribiendo. */
fieldset:focus-within { border-color: var(--border-strong); }

/* .form-columns pone .hint-inline en display:block, lo que dentro de un
   <legend> parte el titulo en dos renglones. Necesita repetir .form-columns:
   con "legend .hint-inline" a secas pierde por especificidad y no aplica. */
.form-columns legend .hint-inline { display: inline; margin-top: 0; }

/* --- Avisos del formulario -------------------------------------------------
   Lo que estaba como <p class="muted"> era justamente lo mas importante de la
   pantalla cuando el bot corre: que se puede guardar en caliente y que no. */
.form-notice {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 0 0 16px;
    padding: 11px 14px;
    border-radius: 8px;
    font-size: 13px;
    line-height: 1.5;
    background: var(--warn-soft);
    border: 1px solid rgba(240, 169, 58, 0.35);
    color: var(--text);
}
.form-notice svg { flex: none; margin-top: 2px; color: var(--warning); }
.form-notice.is-error {
    background: rgba(239, 68, 68, 0.09);
    border-color: rgba(239, 68, 68, 0.3);
    color: var(--danger-on-tint);
}
.form-notice.is-error svg { color: var(--danger-on-tint); }
.form-notice strong { font-weight: 600; }

/* --- Barra superior del formulario ----------------------------------------
   Reemplaza un style="" en linea. */
.form-toolbar { display: flex; justify-content: flex-end; margin: 0 0 12px; }

/* --- Valor de solo lectura -------------------------------------------------
   El maker fee no es un campo: es un dato leido del exchange. Antes usaba un
   <label> sin control al cual apuntar, que es una etiqueta que no etiqueta
   nada. */
.readonly-value { margin: 10px 0 0; }
.readonly-value .field-label {
    display: block;
    margin-bottom: 3px;
    font-size: 12px;
    line-height: 1.3;
    color: var(--text-muted);
}
.readonly-value .field-value { font-size: 13px; font-variant-numeric: tabular-nums; }

/* --- Barra de guardado fija ------------------------------------------------
   Treinta campos en ocho fichas: con el boton solo hasta abajo, cambiar un
   valor de la primera ficha obligaba a recorrer todo el formulario para
   guardarlo. */
.form-actions.is-sticky {
    position: sticky;
    bottom: 0;
    z-index: 2;
    gap: 12px;
    align-items: center;
    margin-top: 16px;
    padding: 14px 0;
    background: linear-gradient(to top, var(--bg) 72%, transparent);
}
.form-actions.is-sticky .spacer { flex: 1; }
.form-dirty {
    font-size: 12px;
    color: var(--warning);
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.form-dirty[hidden] { display: none; }
.btn-primary[aria-busy="true"] { opacity: 0.7; cursor: progress; }

/* --- Bandas de precio en dos columnas --------------------------------------
   Cada renglon es una regla completa: umbral a la izquierda, multiplicador a
   la derecha. Leerlas apiladas obligaba a recordar cual multiplicador
   pertenecia a cual precio. */
.band-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 12px; }
/* Las etiquetas tienen largos distintos y algunas parten en dos renglones;
   con la etiqueta creciendo, los dos campos de un mismo renglon quedan
   alineados por abajo en vez de escalonados. */
.band-cell { display: flex; flex-direction: column; }
.band-cell label { flex: 1; margin-top: 0; }
.col-right { text-align: right; }

@media (max-width: 760px) {
    /* Bajo 760 px la tabla conserva lo que decide algo (bot, estado,
       acciones) y esconde lo que se consulta (capas, mid, símbolo). Las
       acciones quedan fijas a la derecha para no perderse al desplazar. */
    .ops-table .col-secondary { display: none; }
    .ops-table .row-actions { position: sticky; right: 0; background: var(--surface); box-shadow: -12px 0 12px -8px rgba(0,0,0,0.6); }
    .ops-table .cell-id { min-width: 120px; }
}

/* Bot Futuros en móvil: cada bot es una tarjeta de cuatro filas
   (bot · símbolo · dir / nocional · capas / estado / botones) en lugar de
   una fila de tabla que desplaza y pisa los botones sobre los datos. */
@media (max-width: 760px) {
    .ops-table.is-futuros thead { display: none; }
    .ops-table.is-futuros tbody tr {
        display: grid;
        grid-template-columns: 1fr auto auto;
        grid-template-areas:
            "bot symbol dir"
            "notional notional notional"
            "capas capas capas"
            "status status status"
            "actions actions actions";
        gap: 8px 14px;
        align-items: center;
        padding: 12px 14px;
        border-bottom: 1px solid var(--border);
    }
    .ops-table.is-futuros tbody tr:last-child { border-bottom: none; }
    .ops-table.is-futuros tbody td { display: block; padding: 0; border: 0; width: auto; white-space: normal; }
    .ops-table.is-futuros td.col-secondary { display: block; }
    .ops-table.is-futuros .cell-id { grid-area: bot; min-width: 0; }
    .ops-table.is-futuros .col-symbol { grid-area: symbol; }
    .ops-table.is-futuros .col-dir { grid-area: dir; text-align: right; }
    .ops-table.is-futuros .col-notional { grid-area: notional; text-align: left; }
    .ops-table.is-futuros .col-notional .cell-sub { display: inline; margin-left: 10px; }
    .ops-table.is-futuros .cell-ruler { grid-area: capas; min-width: 0; text-align: center; }
    .ops-table.is-futuros .cell-ruler .ruler { max-width: 100%; height: auto; margin: 0 auto; }
    .ops-table.is-futuros td.col-avg, .ops-table.is-futuros td.col-mid { display: none; }
    .ops-table.is-futuros .col-status { grid-area: status; text-align: left; }
    /* Botones centrados y del mismo ancho: tres columnas iguales, cada una
       se ajusta al botón más largo ("Reconciliar"). */
    .ops-table.is-futuros .row-actions {
        grid-area: actions; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px;
        justify-content: center; position: static; box-shadow: none; padding-top: 4px; max-width: 360px; margin: 0 auto;
    }
    .ops-table.is-futuros .row-actions form { display: contents; }
    .ops-table.is-futuros .row-actions .btn-action,
    .ops-table.is-futuros .row-actions a.btn-action { width: 100%; min-width: 0; }
    .ops-table.is-futuros .error-row-tr { display: block; }
    .ops-table.is-futuros .error-row-tr td { display: block; padding: 8px 14px !important; }

    /* Bot Spot en móvil, misma idea:
       bot · símbolo · posición máx. / escalera / excluido · acumulado /
       estado · fase / botones (2 columnas: hasta 4 botones). */
    .ops-table.is-spot thead { display: none; }
    .ops-table.is-spot tbody tr {
        display: grid;
        grid-template-columns: 1fr auto auto;
        grid-template-areas:
            "bot symbol maxpos"
            "ladder ladder ladder"
            "excluded excluded balance"
            "status status phase"
            "actions actions actions";
        gap: 8px 14px;
        align-items: center;
        padding: 12px 14px;
        border-bottom: 1px solid var(--border);
    }
    .ops-table.is-spot tbody tr:last-child { border-bottom: none; }
    .ops-table.is-spot tbody td { display: block; padding: 0; border: 0; width: auto; white-space: normal; }
    .ops-table.is-spot td.col-secondary { display: block; }
    .ops-table.is-spot .cell-id { grid-area: bot; min-width: 0; }
    .ops-table.is-spot .col-symbol { grid-area: symbol; }
    .ops-table.is-spot .col-maxpos { grid-area: maxpos; text-align: right; }
    .ops-table.is-spot .col-ladder { grid-area: ladder; text-align: center; }
    .ops-table.is-spot .col-ladder .cell-sub { white-space: normal; }
    .ops-table.is-spot .col-excluded { grid-area: excluded; text-align: left; }
    .ops-table.is-spot .col-balance { grid-area: balance; text-align: right; }
    /* Sin encabezado de tabla, dos cifras en LIT no se distinguen: cada una
       lleva su título encima (solo en móvil). */
    .ops-table.is-spot .col-excluded::before,
    .ops-table.is-spot .col-balance::before {
        display: block; font-family: var(--font-ui); font-size: 11px; letter-spacing: 0.05em;
        text-transform: uppercase; color: var(--text-muted); margin-bottom: 2px;
    }
    .ops-table.is-spot .col-excluded::before { content: "Excluido"; }
    .ops-table.is-spot .col-balance::before { content: "Acumulado"; }
    .ops-table.is-spot .col-maxpos::after {
        content: "Máx"; display: block; font-family: var(--font-ui); font-size: 11px; letter-spacing: 0.05em;
        text-transform: uppercase; color: var(--text-muted); margin-top: 2px;
    }
    .ops-table.is-spot .col-status { grid-area: status; text-align: left; }
    .ops-table.is-spot .col-phase { grid-area: phase; text-align: right; }
    .ops-table.is-spot .row-actions {
        grid-area: actions; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px;
        position: static; box-shadow: none; padding-top: 4px; max-width: 360px; margin: 0 auto; width: 100%;
    }
    .ops-table.is-spot .row-actions form { display: contents; }
    .ops-table.is-spot .row-actions .btn-action,
    .ops-table.is-spot .row-actions a.btn-action { width: 100%; min-width: 0; }
    .ops-table.is-spot .error-row-tr { display: block; }
    .ops-table.is-spot .error-row-tr td { display: block; padding: 8px 14px !important; }

    /* Exchanges en móvil: nombre · exchange / detalle / estado / botones
       ("Revisar conexión" ocupa la fila entera; Editar y Eliminar debajo). */
    .ops-table.is-exchanges thead { display: none; }
    .ops-table.is-exchanges tbody tr {
        display: grid;
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "name exchange"
            "detail detail"
            "status status"
            "actions actions";
        gap: 8px 14px;
        align-items: center;
        padding: 12px 14px;
        border-bottom: 1px solid var(--border);
    }
    .ops-table.is-exchanges tbody tr:last-child { border-bottom: none; }
    .ops-table.is-exchanges tbody td { display: block; padding: 0; border: 0; width: auto; white-space: normal; }
    .ops-table.is-exchanges .col-name { grid-area: name; font-weight: 600; }
    .ops-table.is-exchanges .col-exchange { grid-area: exchange; text-align: right; }
    .ops-table.is-exchanges .col-detail { grid-area: detail; font-family: var(--font-num); font-size: 12px; }
    .ops-table.is-exchanges .col-status { grid-area: status; text-align: left; }
    .ops-table.is-exchanges .row-actions {
        grid-area: actions; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px;
        position: static; box-shadow: none; padding-top: 4px; max-width: 360px; margin: 0 auto; width: 100%;
    }
    .ops-table.is-exchanges .row-actions form { display: contents; }
    .ops-table.is-exchanges .row-actions .btn-action,
    .ops-table.is-exchanges .row-actions a.btn-action { width: 100%; min-width: 0; }
    .ops-table.is-exchanges .row-actions .action-wide .btn-action { grid-column: 1 / -1; }
}

@media (max-width: 520px) {
    .band-grid { grid-template-columns: 1fr; gap: 0; }
    .band-cell label { flex: none; }
}

/* --- Direccion del bot: el campo se tine segun lo elegido -------------------
   Es el unico parametro de la ficha General que cambia hacia donde apuesta el
   bot, y se leia igual que un desplegable cualquiera. Mismo verde y rojo que
   la columna Direccion de la lista de bots, para que el color signifique lo
   mismo en las dos pantallas.

   CSS puro: :has() + option:checked se reevalua solo al cambiar la seleccion,
   sin JavaScript que mantener. En un navegador sin :has() el campo se ve
   normal — se pierde el color, no la funcion. */
#f-direction:has(option[value="long"]:checked) {
    background: var(--long-soft);
    border-color: rgba(49, 199, 106, 0.5);
    color: var(--long);
    font-weight: 600;
}
#f-direction:has(option[value="short"]:checked) {
    background: var(--short-soft);
    border-color: rgba(239, 83, 80, 0.5);
    color: var(--danger-on-tint);
    font-weight: 600;
}
/* Bloqueado con el bot corriendo: conserva el color (sigue diciendo hacia
   donde opera) pero apagado, para no contradecir al resto de la ficha. */
#f-direction:disabled:has(option[value="long"]:checked) {
    background: rgba(34, 197, 94, 0.09);
    border-color: rgba(34, 197, 94, 0.3);
    color: #3ba55f;
}
#f-direction:disabled:has(option[value="short"]:checked) {
    background: rgba(239, 68, 68, 0.09);
    border-color: rgba(239, 68, 68, 0.3);
    color: #c25555;
}

/* Vista previa del grid spot (spot_bot_form.html) — hasta 100 niveles,
   así que el cuerpo de la tabla se limita en alto y hace scroll; el
   encabezado y el total quedan siempre visibles. */
.grid-preview-scroll {
    max-height: 340px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: 8px;
}
#grid-preview { margin: 0; }
#grid-preview thead th {
    position: sticky;
    top: 0;
    background: var(--surface);
    z-index: 1;
}
#grid-preview tfoot td {
    border-top: 1px solid var(--border);
    background: var(--hover);
}

/* Precio en vivo de LIT en "Vista previa del grid" — es el dato que ancla
   toda la lectura de la tabla, así que se separa de .hint-inline (11px) y se
   destaca en vez de leerse como texto de apoyo. */
.grid-preview-price {
    margin: 0 0 10px;
    font-size: 15px;
    line-height: 1.3;
    color: var(--text-muted);
}
.grid-preview-price #grid-preview-price-value {
    font-weight: 700;
    font-size: 17px;
    color: var(--accent);
    font-variant-numeric: tabular-nums;
}

/* Entrada promedio del grid lleno — misma jerarquía que el precio en vivo:
   es el otro numero clave para leer la tabla (junto al precio, dice si el
   grid como esta configurado entra en perdida o no). */
.grid-preview-avg {
    margin: 10px 0 0;
    font-size: 15px;
    line-height: 1.4;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}


/* --- Superadmin: filas de otros usuarios y filtro "Solo míos" ----------------- */
.page-header-actions { display: flex; align-items: center; gap: 10px; }
tr[data-foreign] { background: rgba(155, 110, 245, 0.10); }
.ops-table tbody tr[data-foreign]:hover, .data-table tbody tr[data-foreign]:hover { background: rgba(155, 110, 245, 0.18); }
:root[data-theme="light"] tr[data-foreign] { background: rgba(124, 77, 230, 0.12); }
body.only-mine tr[data-foreign] { display: none !important; }
@media (max-width: 760px) {
    .page-header-actions { width: 100%; justify-content: space-between; }
}


/* --- Pantalla completa (solo móvil; el navegador exige un toque) ----------- */
.fullscreen-toggle { display: none; }
@media (max-width: 760px) {
    .fullscreen-toggle:not([hidden]) { display: inline-flex; }
}


/* --- Portfolio ------------------------------------------------------------- */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 24px; }
.stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 14px 16px; display: flex; flex-direction: column; gap: 6px; }
.stat-label { color: var(--text-muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; }
.stat-value { font-size: 20px; font-weight: 600; letter-spacing: -0.01em; }
.stat-sub { color: var(--text-muted); font-size: 12px; }
.is-long { color: var(--long); }
.is-short { color: var(--short); }
.section-title { font-size: 15px; font-weight: 600; margin: 28px 0 12px; letter-spacing: -0.01em; }
@media (max-width: 760px) {
    .stat-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; }
    .stat-value { font-size: 17px; }
    .ops-table.is-portfolio, .ops-table.is-positions { display: table; }
    .ops-table.is-portfolio th, .ops-table.is-positions th,
    .ops-table.is-portfolio td, .ops-table.is-positions td { padding: 8px 6px; font-size: 12px; }
}

/* Portfolio: con "Solo míos" los totales pasan a ser solo del superadmin. */
.stat-grid-mine { display: none; }
body.only-mine .stat-grid-mine { display: grid; }
body.only-mine .stat-grid-all { display: none; }
