:root {
  --text: #6b6375;
  --text-h: #08060d;
  --bg: #fff;
  --border: #e5e4e7;
  --code-bg: #f4f3ec;
  --accent: #aa3bff;
  --accent-bg: rgba(170, 59, 255, 0.1);
  --accent-border: rgba(170, 59, 255, 0.5);
  --social-bg: rgba(244, 243, 236, 0.5);
  --shadow:
    rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;

  --sans: system-ui, 'Segoe UI', Roboto, sans-serif;
  --heading: system-ui, 'Segoe UI', Roboto, sans-serif;
  --mono: ui-monospace, Consolas, monospace;

  font: 18px/145% var(--sans);
  letter-spacing: 0.18px;
  color-scheme: light dark;
  color: var(--text);
  background: var(--bg);
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

  @media (max-width: 1024px) {
    font-size: 16px;
  }
}

@media (prefers-color-scheme: dark) {
  :root {
    --text: #9ca3af;
    --text-h: #f3f4f6;
    --bg: #16171d;
    --border: #2e303a;
    --code-bg: #1f2028;
    --accent: #c084fc;
    --accent-bg: rgba(192, 132, 252, 0.15);
    --accent-border: rgba(192, 132, 252, 0.5);
    --social-bg: rgba(47, 48, 58, 0.5);
    --shadow:
      rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
  }

  #social .button-icon {
    filter: invert(1) brightness(2);
  }
}

#root {
  /* Ancho fijo original del template de Vite (1126px) sacado a propósito:
     con una tabla tan ancha, ese límite hacía que el contenido se desbordara
     hacia la derecha y la página quedara con scroll horizontal global, dando
     la sensación de un margen "sobrante" a la izquierda. Ver docs/07-Frontend.md. */
  width: 100%;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}

body {
  margin: 0;
}

h1,
h2 {
  font-family: var(--heading);
  font-weight: 500;
  color: var(--text-h);
}

h1 {
  font-size: 56px;
  letter-spacing: -1.68px;
  margin: 32px 0;
  @media (max-width: 1024px) {
    font-size: 36px;
    margin: 20px 0;
  }
}
h2 {
  font-size: 24px;
  line-height: 118%;
  letter-spacing: -0.24px;
  margin: 0 0 8px;
  @media (max-width: 1024px) {
    font-size: 20px;
  }
}
p {
  margin: 0;
}

code,
.counter {
  font-family: var(--mono);
  display: inline-flex;
  border-radius: 4px;
  color: var(--text-h);
}

code {
  font-size: 15px;
  line-height: 135%;
  padding: 4px 8px;
  background: var(--code-bg);
}
.app {
  max-width: 1600px;
  margin: 0 auto;
  padding: 1.5rem;
  padding-bottom: 2.5rem; /* deja aire para que la barra fija de abajo no tape la última fila */
  font-family: system-ui, sans-serif;
  text-align: left;
}

.filters {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1rem;
  flex-wrap: wrap;
}

.filters input,
.filters select {
  padding: 0.4rem 0.6rem;
}

/* La tabla puede ser más ancha que la pantalla (muchas columnas de servicios);
   este wrapper contiene el scroll horizontal acá adentro en vez de dejar que
   toda la página se desborde (lo que rompía el centrado, ver index.css). El
   scrollbar nativo de este contenedor queda oculto (sigue funcionando el
   scroll con touch/trackpad) porque el control visible es `.sticky-scrollbar`,
   fijo abajo del viewport — ver docs/cambios_frontend.md. */
.table-scroll {
  width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.table-scroll::-webkit-scrollbar {
  display: none;
}

/* Barra de scroll horizontal fija al fondo del viewport, sincronizada por JS
   con el scroll real de `.table-scroll` (App.tsx). Existe para que se pueda
   arrastrar el scroll de la tabla viendo la primera fila, sin tener que bajar
   hasta el final de la tabla para encontrar el scrollbar.
   `left`/`width` se fijan por JS (recalcStickyBar en App.tsx) para que calcen
   exactamente con `.table-scroll`, no con todo el viewport — si no, al ser
   `.app` más angosto que la pantalla en monitores grandes, esta barra tendría
   más recorrido propio que la tabla y el scroll sincronizado se desfasaría
   cerca del final. */
.sticky-scrollbar {
  position: fixed;
  bottom: 0;
  height: 16px;
  overflow-x: auto;
  overflow-y: hidden;
  background: var(--code-bg, #1f2028);
  border-top: 1px solid var(--border, #2e303a);
  z-index: 500;
}

.sticky-scrollbar.is-hidden {
  display: none;
}

.sticky-scrollbar-spacer {
  height: 1px;
}

.sticky-scrollbar::-webkit-scrollbar {
  height: 14px;
}

.sticky-scrollbar::-webkit-scrollbar-track {
  background: transparent;
}

.sticky-scrollbar::-webkit-scrollbar-thumb {
  background: var(--border, #2e303a);
  border-radius: 8px;
}

table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 1rem;
  font-size: 0.85rem;
}

th,
td {
  border: 1px solid #ddd;
  padding: 0.35rem 0.5rem;
  text-align: left;
  white-space: nowrap;
}

th {
  background: #f3f3f3;
  text-align: center;
}

tr.row-clickable {
  cursor: pointer;
}

tr.row-clickable:hover {
  background: rgba(192, 132, 252, 0.1);
}

.text-danger {
  color: #ff6b6b;
  font-weight: 600;
}

.error {
  color: #b00020;
}

td.vencido {
  background: #b00020;
  color: #fff;
  font-weight: bold;
  text-align: center;
}

/* Widget de estado del daemon */
.daemon-status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.25rem;
  background: var(--code-bg, #1f2028);
  border: 1px solid var(--border, #2e303a);
  border-radius: 8px;
  padding: 0.6rem 1rem;
  margin-bottom: 1.5rem;
  font-size: 0.9rem;
}

.daemon-status button {
  padding: 0.35rem 0.9rem;
  cursor: pointer;
}

/* Modal de detalle (`PropertyModal.tsx`).
   Se probaron, en orden, `position: fixed` (varias variantes),
   `createPortal` a `<body>` con `fixed`, y `<dialog>` + `.showModal()`
   (top layer) — los tres fallaron en la práctica (confirmado por el
   usuario en celular real, no solo en devtools), con el mismo síntoma:
   el modal aparece en un punto fijo del documento, no del viewport visible.
   Causa: los tres dependen de que el navegador resuelva el "containing
   block" contra el viewport real, y cualquier ancestro con
   filter/transform/contain — incluido <html>/<body>, fuera de nuestro
   control — puede romper eso, incluso (en la práctica, pese a la
   especificación) para el top layer de `<dialog>`. Ver docs/07-Frontend.md.

   Solución actual: `.modal-overlay` se posiciona con `position: absolute`
   pero con `top`/`left`/`width`/`height` calculados a mano en JS
   (`PropertyModal.tsx`, `scrollX`/`scrollY`/`innerWidth`/`innerHeight`) en
   vez de dejar que el navegador los resuelva — así el resultado es
   correcto sin importar qué ancestro haya terminado siendo el containing
   block, porque el origen (0,0) de ese containing block es siempre el
   borde superior-izquierdo del documento, igual que `scrollX`/`scrollY`. */
.modal-overlay {
  position: absolute;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.6);
  overflow-y: auto;
  overflow-x: hidden; /* nunca scroll horizontal, ni siquiera si algo se desborda */
}

.modal-content {
  position: relative; /* para que .modal-close se ancle a la tarjeta, no al overlay */
  /* Sin esto, el padding y el borde se suman por fuera de `width` (modelo
     content-box, el default de CSS) — en pantallas angostas eso podía
     empujar el modal más ancho que el viewport. */
  box-sizing: border-box;
  background: var(--bg, #16171d);
  color: var(--text, #9ca3af);
  border: 1px solid var(--border, #2e303a);
  border-radius: 8px;
  width: min(92vw, 32rem); /* equivalente aprox. a max-w-lg */
  /* Tope incondicional (no solo en el media query de mobile más abajo): el
     modal nunca puede ser más ancho que `.modal-overlay`, que a su vez está
     calculado en JS al ancho real de la pantalla (`PropertyModal.tsx`) — así
     no depende de que el breakpoint de mobile matchee para no desbordar. */
  max-width: 100%;
  max-height: 90vh;
  overflow-y: auto;
  padding: 1.5rem;
}

.modal-close {
  position: absolute;
  top: 0.5rem;
  right: 0.75rem;
  background: none;
  border: none;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  color: inherit;
}

.modal-header h3 {
  margin: 0 0 0.5rem;
}

.modal-header dl {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.25rem 1rem;
  margin: 0.5rem 0 1rem;
}

.modal-header dl > div {
  display: contents;
}

.modal-header dt {
  font-weight: 600;
  opacity: 0.8;
}

.modal-header dd {
  margin: 0;
}

.modal-services {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.modal-service {
  border: 1px solid var(--border, #2e303a);
  border-radius: 6px;
  padding: 0.5rem 0.75rem;
}

.modal-service.overdue {
  border-color: #b00020;
}

.modal-service-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 600;
  margin-bottom: 0.35rem;
}

.badge-overdue {
  background: #b00020;
  color: #fff;
  border-radius: 4px;
  padding: 0.1rem 0.5rem;
  font-size: 0.75rem;
  font-weight: bold;
}

.modal-service-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.25rem;
  font-size: 0.85rem;
}

/* A pedido explícito (2026-07-25): en celular el modal deja de ser una
   tarjeta centrada y pasa a ocupar toda la pantalla del celular. Esto es
   deliberado, no solo estético: mientras se investigaba por qué el modal
   seguía sin centrarse en celular real incluso ya con `<dialog>` (que debería
   ser inmune a esa clase de bug), no se pudo aislar la causa exacta — pero
   un modal a pantalla completa no tiene "centrado" que se pueda ver mal, así
   que el bug deja de ser visible sea cual sea la causa real.
   El corte "es celular" se hace por dos condiciones (una para portrait, otra
   para landscape) porque un solo `max-width` no alcanza: un celular acostado
   tiene un ancho grande (puede pasar de 768px en modelos grandes) pero una
   altura chica, así que también se entra por `max-height`. */
@media (max-width: 768px), (max-height: 768px) and (orientation: landscape) {
  .modal-content {
    /* 100%/100% del `.modal-overlay` (ya calculado en JS al tamaño exacto
       del viewport real), no `100vw`/`100dvh` de nuevo — evita depender
       otra vez de que el navegador resuelva unidades de viewport contra el
       ancestro correcto. */
    width: 100%;
    max-width: 100%;
    height: 100%;
    max-height: 100%;
    border: none;
    border-radius: 0;
    padding: 1rem;
    /* Letra más grande que en desktop: a pedido explícito del usuario, para
       poder leer el modal en celular sin necesidad de hacer zoom (a
       diferencia de la tabla, que sigue chica — ver docs/07-Frontend.md). */
    font-size: 1.1rem;
    /* Scroll vertical sí (ya lo permite `overflow-y: auto` de la regla base),
       horizontal no — a pedido explícito del usuario. */
    overflow-x: hidden;
  }

  .modal-header dl {
    grid-template-columns: 1fr;
  }

  .modal-header dt {
    margin-top: 0.35rem;
  }

  /* Estos dos usan `rem` (relativo a la raíz del documento, no al `.modal-content`
     de arriba), así que el `font-size` de `.modal-content` no los agranda solo
     por herencia — hay que subirlos acá a mano. */
  .modal-service-fields {
    font-size: 1rem;
  }

  .badge-overdue {
    font-size: 0.9rem;
  }

  /* La tabla sigue necesitando zoom en celular para verse completa (son
     muchas columnas, y `white-space: nowrap` en las celdas es intencional —
     ver docs/cambios_frontend.md, la barra de scroll fija de abajo depende
     de eso) — a pedido del usuario no se rediseña, pero un poco más de
     letra ayuda a que sea legible sin tener que zoomear tanto. */
  table {
    font-size: 0.95rem;
  }
}
