:root {
  --p:#4c662b; --on-p:#ffffff; --pc:#cdeda3; --on-pc:#0f2000;
  --sc:#dce7c8; --on-sc:#404a33;
  --tc:#a9ceff; --on-tc:#001e30;
  --bg:#f9faef; --on-bg:#1a1c16;
  --surf:#f9faef; --s-low:#f3f5e8; --s:#eef1e3; --s-high:#e8ebdd; --s-highest:#e2e5d7;
  --on-surf:#1a1c16; --on-surf-var:#44483d; --outline:#c5c8b9;
  --err:#ba1a1a; --warn:#8a5300;
  --shadow: 0 1px 3px rgba(0,0,0,.10), 0 4px 12px rgba(0,0,0,.06);
}
[data-theme="dark"] {
  --p:#b1d18a; --on-p:#1f3701; --pc:#354e16; --on-pc:#cdeda3;
  --sc:#c0cba1; --on-sc:#2a331c;
  --tc:#89ceff; --on-tc:#00344f;
  --bg:#12140e; --on-bg:#e2e3d8;
  --surf:#12140e; --s-low:#1a1c16; --s:#1e201a; --s-high:#282b24; --s-highest:#33362e;
  --on-surf:#e2e3d8; --on-surf-var:#c5c8b9; --outline:#8f9285;
  --err:#ffb4ab; --warn:#ffb951;
  --shadow: 0 1px 3px rgba(0,0,0,.4), 0 6px 16px rgba(0,0,0,.3);
}
* { box-sizing:border-box; -webkit-tap-highlight-color:transparent; }
html,body { margin:0; }
body {
  font-family:"Roboto Flex",system-ui,sans-serif;
  background:var(--bg); color:var(--on-bg);
  transition:background .4s, color .4s;
}
[ng-cloak] { display:none !important; }
.sym { font-family:"Material Symbols Rounded"; font-weight:normal; font-style:normal;
  line-height:1; letter-spacing:normal; text-transform:none; display:inline-block;
  white-space:nowrap; direction:ltr; }

/* ===== App shell: Navigation Rail (M3 Expressive) + contenido ===== */
.app-shell { display:flex; align-items:flex-start; min-height:100vh; }

.rail {
  flex:none; width:84px; height:100vh; position:sticky; top:0;
  display:flex; flex-direction:column; align-items:center; gap:6px;
  padding:20px 0; background:var(--s-low); transition:background .4s;
}
.rail-item {
  width:64px; display:flex; flex-direction:column; align-items:center; gap:4px;
  text-decoration:none; color:var(--on-surf-var); padding:4px 0 6px; border-radius:20px;
  cursor:pointer; transition:color .25s;
}
.rail-item:active { transform:scale(.94); }
.rail-ind {
  width:56px; height:32px; border-radius:16px; display:grid; place-items:center;
  transition:background .3s cubic-bezier(.2,.9,.3,1.3), color .3s;
}
.rail-item:hover .rail-ind { background:var(--s-high); }
.rail-ind .sym { font-size:22px; }
.rail-label { font-size:12px; font-weight:500; letter-spacing:.1px; }
.rail-item.active { color:var(--on-surf); }
.rail-item.active .rail-ind {
  background:var(--pc); color:var(--on-pc);
  animation:rail-pop .35s cubic-bezier(.2,.9,.3,1.3) both;
}
@keyframes rail-pop { from{ transform:scale(.7); opacity:.5; } to{ transform:scale(1); opacity:1; } }

.rail-theme { margin-top:auto; border:none; background:transparent; font:inherit; }

.app-content { flex:1; display:flex; justify-content:center; min-width:0; }
.content-inner { width:100%; max-width:1160px; padding:0 20px 48px; }

@media (max-width:599px) {
  .rail { width:72px; }
  .rail-item { width:56px; }
  .rail-ind { width:48px; }
  .rail-label { font-size:10.5px; }
}

/* Top app bar */
header {
  position:sticky; top:0; z-index:10; background:var(--bg);
  display:flex; align-items:center; gap:12px; padding:20px 4px 12px;
  transition:background .4s;
}
.logo { width:48px; height:48px; border-radius:16px; background:var(--pc); color:var(--on-pc);
  display:grid; place-items:center; flex:none; }
.logo .sym { font-size:26px; }
.titles { flex:1; min-width:0; }
.titles h1 { font-size:22px; font-weight:700; margin:0; letter-spacing:-.3px; }
.titles .sub { font-size:13px; color:var(--on-surf-var); margin-top:2px; }
.device-wrap { display:inline-flex; align-items:center; gap:4px; margin-top:4px;
  background:var(--s-high); border-radius:999px; padding:2px 6px 2px 10px; }
.device-wrap .mini { font-size:15px; color:var(--on-surf-var); }
.device-select { border:none; background:transparent; color:var(--on-surf); cursor:pointer;
  font-family:inherit; font-size:13px; font-weight:500; padding:4px 2px; outline:none;
  max-width:150px; -webkit-appearance:none; appearance:none; }
.device-select option { color:#000; }
.icon-btn { width:44px; height:44px; border-radius:50%; border:none; background:transparent;
  color:var(--on-surf-var); display:grid; place-items:center; cursor:pointer; flex:none;
  transition:background .2s; }
.icon-btn:hover { background:var(--s-high); }
.icon-btn:active { transform:scale(.92); }
.icon-btn .sym { font-size:24px; }
.spin { animation:spin .6s ease; }
@keyframes spin { to { transform:rotate(360deg); } }

.live { display:inline-flex; align-items:center; gap:6px; background:var(--sc); color:var(--on-sc);
  font-size:12px; font-weight:500; padding:6px 12px 6px 10px; border-radius:999px; }
.dot { width:8px; height:8px; border-radius:50%; background:var(--p); position:relative; }
.dot::after { content:""; position:absolute; inset:-4px; border-radius:50%; background:var(--p);
  opacity:.4; animation:pulse 1.8s ease-out infinite; }
@keyframes pulse { 0%{transform:scale(.6);opacity:.5} 100%{transform:scale(1.8);opacity:0} }
/* El pill del header refleja el contacto real de la placa: verde si responde,
   rojo si lleva mas del umbral en silencio. */
.live.on .dot { background:var(--p); }
.live.down { background:color-mix(in srgb, var(--err) 16%, transparent); color:var(--err); }
.live.down .dot, .live.down .dot::after { background:currentColor; }

/* Resumen superior: hero + metricas extra. En pantallas anchas van en dos
   columnas para repartir el espacio; en moviles se apilan. */
.summary { display:grid; gap:16px; grid-template-columns:1fr; margin-top:6px; }
@media (min-width:900px) {
  .summary { grid-template-columns:minmax(320px,1.1fr) minmax(260px,1fr); align-items:start; }
  .summary.solo { grid-template-columns:1fr; }
}
.summary > * { min-width:0; }
.summary .hero { margin-top:0; }
.summary .extra-metrics { margin-top:0; align-content:start; }

/* Hero metric cards */
.hero { display:grid; grid-template-columns:1fr 1fr; gap:14px; margin-top:6px; }
.metric { border-radius:30px; padding:20px 20px 16px; position:relative; overflow:hidden;
  min-height:158px; display:flex; flex-direction:column; justify-content:space-between;
  box-shadow:var(--shadow); animation:rise .6s cubic-bezier(.2,.9,.3,1.3) both; }
.metric.temp { background:var(--pc); color:var(--on-pc); }
.metric.hum  { background:var(--tc); color:var(--on-tc); }
.metric:nth-child(2){ animation-delay:.08s; }
@keyframes rise { from{opacity:0; transform:translateY(16px) scale(.96);} to{opacity:1;transform:none;} }
.metric .top { display:flex; align-items:center; justify-content:space-between; }
.metric .top .sym { font-size:30px; opacity:.9; }
.metric .label { font-size:14px; font-weight:500; opacity:.85; }
.metric .val { font-size:52px; font-weight:700; letter-spacing:-2px; line-height:1; }
.metric .val .u { font-size:22px; font-weight:500; letter-spacing:0; margin-left:2px; }
.metric .trend { font-size:12px; font-weight:500; display:inline-flex; align-items:center; gap:4px;
  background:rgba(0,0,0,.08); padding:4px 10px; border-radius:999px; align-self:flex-start; }
[data-theme="dark"] .metric .trend { background:rgba(0,0,0,.22); }
.metric .trend .sym { font-size:16px; }

/* Segmented pill */
.segment { display:flex; gap:4px; background:var(--s); padding:4px; border-radius:999px;
  margin:22px 0 14px; }
.segment button { flex:1; border:none; background:transparent; color:var(--on-surf-var);
  font-family:inherit; font-size:14px; font-weight:500; padding:10px; border-radius:999px;
  cursor:pointer; transition:.25s; }
.segment button.active { background:var(--p); color:var(--on-p); box-shadow:var(--shadow); }
.segment .mini { font-size:16px; vertical-align:-3px; margin-right:5px; }
.segment.mode { margin:22px 0 8px; background:var(--s-high); }
.segment.mode + .segment { margin-top:0; }

/* Cards */
.card { background:var(--s-low); border-radius:28px; padding:20px; box-shadow:var(--shadow);
  animation:rise .6s cubic-bezier(.2,.9,.3,1.3) both; animation-delay:.12s; }
.content-inner > .card + .card { margin-top:20px; }
.card h2 { font-size:16px; font-weight:600; margin:0 0 4px; }
.card .cap { font-size:13px; color:var(--on-surf-var); margin:0 0 14px; }
.chart-wrap { position:relative; height:240px; }
air-chart, air-quality-chart { display:block; height:100%; }

/* Rejilla de graficas: llenan el ancho disponible en pantallas grandes.
   Con una sola grafica ocupa todo; con varias se reparten en columnas. */
.charts-grid { display:grid; gap:20px; grid-template-columns:1fr; margin-top:20px; }
@media (min-width:1000px) {
  .charts-grid { grid-template-columns:repeat(auto-fit,minmax(340px,1fr)); align-items:start; }
}
/* min-width:0 evita que el <canvas> de Chart.js expanda su celda del grid y
   provoque scroll horizontal (los items de grid/flex traen min-width:auto). */
.charts-grid > .card { min-width:0; }
.chart-wrap { min-width:0; }

/* ===== Ampliar grafica =====
   La tarjeta pasa a ser un overlay a pantalla completa. Chart.js es responsive,
   asi que al crecer .chart-wrap el canvas se reajusta solo. */
.charts-grid > .card { position:relative; }
.zoom-btn { position:absolute; top:12px; right:12px; z-index:1; color:var(--on-surf-var); }

.zoom-backdrop { position:fixed; inset:0; z-index:60; background:rgba(0,0,0,.55);
  animation:fade-in .2s ease both; }
@keyframes fade-in { from{opacity:0;} to{opacity:1;} }

.card.zoomed {
  position:fixed; inset:12px; z-index:61; margin:0; overflow:auto;
  display:flex; flex-direction:column; animation:none;
}
@media (min-width:1000px) { .card.zoomed { inset:32px 40px; padding:28px; } }
/* La grafica ocupa todo el alto sobrante de la tarjeta ampliada. */
.card.zoomed .chart-wrap { height:auto; flex:1 1 auto; min-height:280px; }
.card.zoomed .zoom-btn { top:16px; right:16px; }

/* Stats chips */
.stats { display:grid; grid-template-columns:repeat(3,1fr); gap:10px; margin-top:16px; }
.stat { background:var(--s); border-radius:22px; padding:14px 12px; text-align:center; }
.stat .k { font-size:12px; color:var(--on-surf-var); display:flex; align-items:center;
  justify-content:center; gap:4px; }
.stat .k .sym { font-size:15px; }
.stat .v { font-size:22px; font-weight:700; margin-top:4px; letter-spacing:-.5px; }
.stat .v .u { font-size:11px; font-weight:500; color:var(--on-surf-var); margin-left:2px; }

/* Metricas adicionales (pm1/pm25/pm4/pm10/presion/voc), cantidad variable */
.extra-metrics { display:grid; grid-template-columns:repeat(auto-fit,minmax(110px,1fr)); gap:10px; margin-top:14px; }

footer { text-align:center; color:var(--on-surf-var); font-size:12px; margin-top:26px; }
.empty { text-align:center; color:var(--on-surf-var); padding:40px 20px; }
.empty .sym { font-size:40px; display:block; margin-bottom:8px; opacity:.6; }

/* ===== Inicio: resumen y accesos ===== */
.feature-list { display:flex; flex-direction:column; gap:8px; margin-top:16px; }
.feature { display:flex; align-items:center; gap:14px; background:var(--s); border-radius:20px;
  padding:14px 16px; text-decoration:none; color:var(--on-surf); transition:background .2s; }
.feature:hover { background:var(--s-high); }
.feature:active { transform:scale(.98); }
.feature > .sym:first-child { font-size:24px; color:var(--p); flex:none; }
.feature .txt { flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
.feature .txt strong { font-size:15px; font-weight:600; }
.feature .txt span { font-size:13px; color:var(--on-surf-var); }
.feature .chev { flex:none; color:var(--on-surf-var); }

/* ===== Administración de estaciones ===== */
.icon-btn.sm { width:38px; height:38px; }
.icon-btn.sm .sym { font-size:20px; }
.icon-btn.danger:hover { background:color-mix(in srgb, var(--err) 18%, transparent); color:var(--err); }

/* Botones */
.btn { display:inline-flex; align-items:center; gap:6px; border:none; cursor:pointer;
  font-family:inherit; font-size:14px; font-weight:500; padding:11px 18px; border-radius:999px;
  transition:.2s; }
.btn:active { transform:scale(.96); }
.btn.primary { background:var(--p); color:var(--on-p); box-shadow:var(--shadow); }
.btn.ghost { background:var(--s-high); color:var(--on-surf); }
.btn .mini { font-size:18px; }

/* Barra de acciones */
.admin-bar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin:18px 0 6px; }
.token-wrap { display:inline-flex; align-items:center; gap:6px; background:var(--s-high);
  border-radius:999px; padding:2px 12px; margin-left:auto; }
.token-wrap .mini { font-size:16px; color:var(--on-surf-var); }
.token-wrap input { border:none; background:transparent; color:var(--on-surf); outline:none;
  font-family:inherit; font-size:13px; padding:9px 2px; width:150px; }

/* Banner */
.banner { border-radius:16px; padding:12px 16px; font-size:14px; margin:10px 0; }
.banner.err { background:color-mix(in srgb, var(--err) 15%, transparent); color:var(--err); }

/* Formulario */
.form-card h3.sec { font-size:13px; font-weight:600; color:var(--on-surf-var);
  text-transform:uppercase; letter-spacing:.4px; margin:20px 0 10px; }
.grid2 { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.field { display:flex; flex-direction:column; gap:5px; font-size:13px; }
.field.span2 { grid-column:1 / -1; }
.field > span { color:var(--on-surf-var); font-weight:500; }
.field input { border:1px solid var(--outline); background:var(--surf); color:var(--on-surf);
  border-radius:12px; padding:11px 12px; font-family:inherit; font-size:14px; outline:none;
  transition:border-color .2s; }
.field input:focus { border-color:var(--p); }
.field input:disabled { opacity:.6; cursor:not-allowed; }

/* Chips de parámetros */
.chips { display:flex; flex-wrap:wrap; gap:8px; }
.chip { display:inline-flex; align-items:center; gap:6px; background:var(--s-high);
  color:var(--on-surf-var); border-radius:999px; padding:8px 14px; font-size:13px; font-weight:500;
  cursor:pointer; transition:.2s; user-select:none; }
.chip input { display:none; }
.chip.on { background:var(--pc); color:var(--on-pc); }
.chips.readonly { margin-top:12px; }
.chips.readonly .chip { cursor:default; }

/* Switch */
.switch { display:inline-flex; align-items:center; gap:8px; margin-top:18px; font-size:14px;
  cursor:pointer; }
.switch input { width:18px; height:18px; accent-color:var(--p); }

.form-actions { display:flex; justify-content:flex-end; gap:10px; margin-top:22px; }

/* Lista de estaciones */
.est-list { display:flex; flex-direction:column; gap:14px; margin-top:14px; }
.card.est { padding:18px 20px; }
.est-head { display:flex; align-items:center; justify-content:space-between; gap:8px; }
.est-id { display:flex; align-items:center; gap:10px; min-width:0; }
.est-id strong { font-size:17px; letter-spacing:-.2px; overflow:hidden; text-overflow:ellipsis; }
.est-btns { display:flex; gap:2px; flex:none; }
.badge { font-size:11px; font-weight:600; padding:4px 10px; border-radius:999px; flex:none; }
.badge.ok { background:var(--sc); color:var(--on-sc); }
.badge.off { background:var(--s-high); color:var(--on-surf-var); }
.badge.role { background:var(--pc); color:var(--on-pc); }
.est-name { font-size:14px; font-weight:500; margin-top:8px; }
.est-desc { font-size:13px; color:var(--on-surf-var); margin-top:2px; }
.est-meta { display:flex; flex-wrap:wrap; gap:14px; margin-top:12px; font-size:12px;
  color:var(--on-surf-var); }
.est-meta .mini { font-size:14px; vertical-align:-2px; margin-right:3px; }

@media (max-width:480px) { .grid2 { grid-template-columns:1fr; } }

/* ===== Login (pagina standalone, sin rail) ===== */
.auth-shell { min-height:100vh; display:flex; align-items:center; justify-content:center; padding:24px; }
.auth-card { width:100%; max-width:380px; text-align:center; }
.auth-card .logo { margin:0 auto; }
.auth-card h1 { font-size:20px; margin:14px 0 4px; }
.auth-card .sub { font-size:13px; color:var(--on-surf-var); margin:0 0 22px; }
.auth-card form { display:flex; flex-direction:column; gap:14px; text-align:left; }
.auth-card .btn.primary { width:100%; justify-content:center; margin-top:6px; }

.rail-logout { border:none; background:transparent; font:inherit; }

/* ===== Monitoreo de placas (pagina de estaciones) ===== */
/* Bloque de salud de cada estacion: dice si sigue viva, por que se reinicio
   la ultima vez y que sensores respondieron. */
.banner.info { background:color-mix(in srgb, var(--tc) 30%, transparent); color:var(--on-tc); }

.badge.live { display:inline-flex; align-items:center; gap:6px; }
.badge.live .dot { width:7px; height:7px; border-radius:50%; background:currentColor; flex:none; }
.badge.live.on { background:color-mix(in srgb, var(--p) 18%, transparent); color:var(--p); }
/* "Sin señal" tiene que saltar a la vista: es el motivo de existir del panel. */
.badge.live.down { background:color-mix(in srgb, var(--err) 16%, transparent); color:var(--err); }
.badge.live.down .dot { animation:pulso 1.6s ease-in-out infinite; }
@keyframes pulso { 0%,100% { opacity:1; } 50% { opacity:.25; } }

.icon-btn.sm.warn:hover { background:color-mix(in srgb, var(--warn) 18%, transparent); color:var(--warn); }

.mon { margin-top:14px; padding-top:14px; border-top:1px solid var(--outline); }
.mon-grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(170px, 1fr)); gap:12px 18px; }
.mon-item { display:flex; flex-direction:column; gap:2px; min-width:0; }
.mon-item .k { font-size:11px; text-transform:uppercase; letter-spacing:.4px;
  color:var(--on-surf-var); }
.mon-item .v { font-size:14px; font-weight:500; }
.mon-item .v em { font-style:normal; font-weight:400; font-size:12px; color:var(--on-surf-var); }
.mon-item .v.alerta { color:var(--err); }

.mon-item .v.sensores { display:flex; flex-wrap:wrap; gap:6px; }
.mon-item .v.sensores b { font-size:11px; font-weight:600; padding:2px 8px; border-radius:999px; }
.mon-item .v.sensores b.si { background:var(--sc); color:var(--on-sc); }
.mon-item .v.sensores b.no { background:color-mix(in srgb, var(--err) 16%, transparent); color:var(--err); }

.mon-cmd { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:12px;
  padding:8px 12px; border-radius:12px; font-size:13px;
  background:color-mix(in srgb, var(--warn) 14%, transparent); color:var(--warn); }
.btn.xs { padding:4px 12px; font-size:12px; margin-left:auto; }

/* Bitacora: los logs que la placa sube en cada heartbeat. */
.bitacora { margin-top:14px; padding-top:12px; border-top:1px solid var(--outline);
  max-height:420px; overflow-y:auto; display:flex; flex-direction:column; gap:8px; }
.empty.sm { padding:16px; font-size:13px; }
.evento { border-left:3px solid var(--outline); padding:6px 0 6px 10px; }
.evento.t-arranque { border-color:var(--warn); }
.evento.t-offline, .evento.t-error { border-color:var(--err); }
.evento.t-online { border-color:var(--p); }
.evento-head { display:flex; gap:10px; align-items:baseline; font-size:11px;
  color:var(--on-surf-var); }
.evento-head .tipo { text-transform:uppercase; letter-spacing:.4px; font-weight:600; }
.evento-msg { font-size:13px; margin-top:2px; }
.evento-log { margin:6px 0 0; padding:8px 10px; border-radius:10px; background:var(--s-high);
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:11px; line-height:1.5;
  white-space:pre-wrap; word-break:break-word; color:var(--on-surf-var); }

@media (max-width:480px) { .mon-grid { grid-template-columns:1fr 1fr; } }
