:root{--bg:#04060d;--fg:#cfe9ff;--dim:#6f86a8;--up:#19f0c9;--deg:#ffb020;--down:#ff3860;--line:rgba(90,200,255,.2);
--mono:ui-monospace,"JetBrains Mono","SF Mono",Menlo,Consolas,"Liberation Mono",monospace}
*{box-sizing:border-box}
html,body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--mono);min-height:100%}
body{overflow-x:hidden;position:relative}
.bg-grid{position:fixed;inset:0;z-index:-3;overflow:hidden;mask-image:radial-gradient(ellipse at 50% 40%,#000 25%,transparent 78%)}
.bg-grid::before{content:"";position:absolute;inset:-44px;background-image:linear-gradient(rgba(0,240,255,.055) 1px,transparent 1px),linear-gradient(90deg,rgba(0,240,255,.055) 1px,transparent 1px);background-size:44px 44px;animation:grid 18s linear infinite;will-change:transform}
.bg-glow{position:fixed;inset:-20%;z-index:-4;background:radial-gradient(40% 35% at 20% 25%,rgba(34,211,238,.13),transparent 70%),radial-gradient(40% 40% at 80% 75%,rgba(167,139,250,.14),transparent 70%);animation:drift 22s ease-in-out infinite alternate}
.scan{position:fixed;inset:0;z-index:5;pointer-events:none;background:repeating-linear-gradient(0deg,rgba(255,255,255,.03) 0 1px,transparent 1px 3px)}
.scan::after{content:"";position:absolute;left:0;right:0;top:0;height:140px;will-change:transform;background:linear-gradient(180deg,transparent,rgba(25,240,201,.07),transparent);animation:sweep 7s linear infinite}
@keyframes grid{to{transform:translate(44px,44px)}}
@keyframes drift{to{transform:translate(4%,3%) scale(1.08)}}
@keyframes sweep{from{transform:translateY(-160px)}to{transform:translateY(100vh)}}

.hud{display:grid;grid-template-columns:1fr auto 1fr;gap:16px;align-items:center;padding:20px 28px 6px}
.brand h1{margin:0;font-size:clamp(22px,3.4vw,40px);letter-spacing:.42em;font-weight:700;color:#e8f6ff;text-shadow:0 0 18px rgba(34,211,238,.55)}
.glitch{position:relative}
.glitch::before,.glitch::after{content:attr(data-text);position:absolute;inset:0;overflow:hidden;opacity:.7}
.glitch::before{color:#22d3ee;transform:translate(2px,0);clip-path:inset(0 0 62% 0);animation:gl1 5.5s infinite steps(1)}
.glitch::after{color:#ff3860;transform:translate(-2px,0);clip-path:inset(60% 0 0 0);animation:gl2 5.5s infinite steps(1)}
@keyframes gl1{0%,90%,100%{transform:translate(0)}91%{transform:translate(3px,-1px)}93%{transform:translate(-2px,1px)}95%{transform:translate(2px,0)}}
@keyframes gl2{0%,92%,100%{transform:translate(0)}93%{transform:translate(-3px,1px)}96%{transform:translate(2px,-1px)}}
.sub{margin:6px 0 0;font-size:12px;letter-spacing:.35em;color:var(--dim)}
.cursor{color:var(--up);animation:blink 1s steps(1) infinite}
@keyframes blink{50%{opacity:0}}
.stats{display:flex;gap:12px}
.stat{min-width:104px;padding:10px 14px;border:1px solid var(--line);background:rgba(8,16,32,.55);backdrop-filter:blur(6px);clip-path:polygon(10px 0,100% 0,100% calc(100% - 10px),calc(100% - 10px) 100%,0 100%,0 10px);text-align:center}
.stat b{display:block;font-size:30px;line-height:1.1}
.stat span{font-size:10px;letter-spacing:.18em;color:var(--dim)}
.stat.up b{color:var(--up);text-shadow:0 0 14px var(--up)}
.stat.degraded b{color:var(--deg);text-shadow:0 0 14px var(--deg)}
.stat.down b{color:var(--down);text-shadow:0 0 14px var(--down)}
.clock{text-align:right}
.clock b{display:block;font-size:22px;letter-spacing:.12em}
.clock span{font-size:10px;letter-spacing:.22em;color:var(--dim)}
.banner{margin:8px 28px;padding:10px 14px;border:1px solid var(--down);color:var(--down);background:rgba(255,56,96,.08);letter-spacing:.12em;font-size:12px;animation:blink 1.2s steps(1) infinite}
.banner[hidden]{display:none}

main{padding:0 20px 20px}
.stage{position:relative}
#graph{width:100%;height:auto;display:block;max-height:calc(100vh - 150px)}

/* aristas */
.edge .line{fill:none;stroke:var(--line);stroke-width:1.2;transition:stroke .3s,opacity .3s}
.edge .flow{fill:none;stroke:var(--up);stroke-width:1.7;stroke-dasharray:2 13;stroke-linecap:round;opacity:.75;animation:flow 1.5s linear infinite}
.edge .dot{fill:#fff;filter:drop-shadow(0 0 5px var(--up))}
.edge.bad .flow{stroke:var(--down);stroke-dasharray:7 7;animation:flow .5s linear infinite,blink .9s steps(1) infinite}
.edge.bad .line{stroke:rgba(255,56,96,.45)}
.edge.dead .dot{display:none}
.edge.dead .flow{opacity:.15;animation:none}
@keyframes flow{to{stroke-dashoffset:-30}}
#graph.dim .edge{opacity:.12}
#graph.dim .node{opacity:.28}
#graph.dim .edge.hl{opacity:1}
#graph.dim .edge.hl .line{stroke:rgba(255,255,255,.55);stroke-width:1.8}
#graph.dim .node.hl{opacity:1}

/* nodos */
.node{--c:#5b6b8a;cursor:default}
.node.up{--c:var(--up)}.node.degraded{--c:var(--deg)}.node.down{--c:var(--down)}
.node.link{cursor:pointer}
.node .inner{animation:boot .7s cubic-bezier(.2,.8,.2,1) both;animation-delay:var(--d,0ms);transform-box:fill-box;transform-origin:center}
@keyframes boot{from{opacity:0;transform:scale(.2)}to{opacity:1;transform:scale(1)}}
.node .hex{fill:rgba(6,14,28,.92);stroke:var(--c);stroke-width:2;filter:drop-shadow(0 0 7px var(--c));transition:fill .25s}
.node:hover .hex,.node.sel .hex{fill:rgba(20,40,70,.95)}
.node .gring{fill:none;stroke:var(--g);stroke-width:2;stroke-dasharray:26 8;opacity:.8;transform-box:fill-box;transform-origin:center;animation:spin 14s linear infinite}
.node .pulse{fill:none;stroke:var(--c);stroke-width:1.5;transform-box:fill-box;transform-origin:center;animation:pulse 2.8s ease-out infinite}
.node.down .pulse{animation-duration:.9s}
.node.down .hex{animation:shake .35s steps(2) infinite}
.node.degraded .pulse{animation-duration:1.6s}
@keyframes pulse{from{opacity:.8;transform:scale(.85)}to{opacity:0;transform:scale(1.9)}}
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes shake{0%{transform:translate(0)}50%{transform:translate(1.5px,-1px)}100%{transform:translate(-1.5px,1px)}}
.node .ico{fill:var(--c);font-size:13px;font-weight:700;text-anchor:middle;dominant-baseline:central;letter-spacing:.04em;pointer-events:none}
.node .lbl{fill:#cfe9ff;font-size:12px;text-anchor:middle;letter-spacing:.05em;pointer-events:none;paint-order:stroke;stroke:#04060d;stroke-width:4px}
.node .lnk{fill:none;stroke:var(--c);stroke-width:1.4;pointer-events:none;opacity:.9}

/* tooltip */
.tip{position:absolute;left:16px;bottom:16px;max-width:360px;padding:14px 16px;border:1px solid var(--line);background:rgba(6,12,24,.88);backdrop-filter:blur(8px);font-size:12px;line-height:1.55;clip-path:polygon(12px 0,100% 0,100% calc(100% - 12px),calc(100% - 12px) 100%,0 100%,0 12px);z-index:3}
.tip[hidden]{display:none}
.tip h3{margin:0 0 4px;font-size:15px;letter-spacing:.08em}
.tip .st{font-weight:700;letter-spacing:.14em}
.tip .row{color:var(--dim)}
.tip .row b{color:var(--fg);font-weight:400}
.tip.up .st{color:var(--up)}.tip.degraded .st{color:var(--deg)}.tip.down .st{color:var(--down)}

.legend{display:flex;flex-wrap:wrap;gap:8px 18px;justify-content:center;padding:4px 0 14px;font-size:11px;letter-spacing:.1em;color:var(--dim)}
.lg::before{content:"";display:inline-block;width:9px;height:9px;margin-right:7px;border-radius:50%;background:currentColor;box-shadow:0 0 8px currentColor}
.lg.up{color:var(--up)}.lg.degraded{color:var(--deg)}.lg.down{color:var(--down)}.lg.flow{color:var(--dim)}.lg.flow::before{border-radius:0;height:2px;width:14px;background:var(--up);box-shadow:none}

.list{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:10px}
.list h4{grid-column:1/-1;margin:14px 0 0;font-size:11px;letter-spacing:.3em;color:var(--dim);font-weight:400;border-bottom:1px solid var(--line);padding-bottom:6px}
.chip{display:flex;align-items:center;gap:10px;padding:9px 12px;border:1px solid var(--line);background:rgba(8,16,32,.55);color:var(--fg);text-decoration:none;font-size:12px;letter-spacing:.04em;transition:border-color .2s,background .2s}
a.chip:hover{border-color:var(--c);background:rgba(20,40,70,.7)}
.chip{--c:#5b6b8a}.chip.up{--c:var(--up)}.chip.degraded{--c:var(--deg)}.chip.down{--c:var(--down)}
.chip i{flex:none;width:9px;height:9px;border-radius:50%;background:var(--c);box-shadow:0 0 9px var(--c)}
.chip.down i{animation:blink .8s steps(1) infinite}
.chip em{margin-left:auto;font-style:normal;color:var(--dim);font-size:11px}
footer{padding:6px 0 26px;text-align:center;font-size:10px;letter-spacing:.25em;color:var(--dim)}

@media (max-width:900px){.hud{grid-template-columns:1fr;text-align:center}.stats{justify-content:center}.clock{text-align:center}#graph{max-height:none}}
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}.edge .dot{display:none}}

/* ---- v2: logos, foco con zoom ---- */
.node{cursor:pointer}
.node .logo{pointer-events:none;transition:filter .4s,opacity .4s}
.node.unknown .logo{opacity:.45}
.node.degraded .logo{filter:sepia(.55) saturate(1.2)}
.node.down .logo{filter:grayscale(1) brightness(.6)}
.node:hover .hex{stroke-width:3.2}
#graph .edge,#graph .node{transition:opacity .45s}
#graph.focus .edge{opacity:.05}
#graph.focus .node{opacity:.13}
#graph.focus .node:not(.hl) .logo,#graph.focus .node:not(.hl) .hex{filter:grayscale(1)}
#graph.focus .edge.hl{opacity:1}
#graph.focus .edge.hl .line{stroke:rgba(255,255,255,.65);stroke-width:2.2}
#graph.focus .edge.hl .flow{stroke-width:2.4}
#graph.focus .node.hl{opacity:1}
#graph.focus .node.sel .gring{stroke-width:3.5;animation-duration:5s}
.tip.pinned{border-color:var(--c,var(--up));box-shadow:0 0 22px rgba(25,240,201,.18)}
.tip.up.pinned{--c:var(--up)}.tip.degraded.pinned{--c:var(--deg)}.tip.down.pinned{--c:var(--down)}
.tip .hint{margin-top:6px;font-size:10px;letter-spacing:.14em}
.tip .open{display:inline-block;margin-top:8px;padding:6px 14px;border:1px solid var(--up);color:var(--up);text-decoration:none;letter-spacing:.2em;font-size:11px;background:rgba(25,240,201,.07)}
.tip .open:hover{background:rgba(25,240,201,.2)}
.node:focus{outline:none}
.node:focus-visible .hex{stroke-width:3.6;stroke:#fff}

/* ---- rendimiento: SVG se repinta entero en cada fotograma, así que solo anima lo que informa ---- */
.node .gring{animation:none}
.node.sel .gring{animation:spin 6s linear infinite}
.node.up .pulse,.node.unknown .pulse{animation:none;opacity:0}
.edge .flow{animation:none}
.edge.bad .flow{animation:flow .5s linear infinite,blink .9s steps(1) infinite}
.idle .edge .dot{display:none}
.idle *,.idle *::before,.idle *::after{animation-play-state:paused!important}

/* ---- capa de puntos viajeros (separada del mapa) ---- */
#flow{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;transform:translateZ(0)}
#flow .dot{fill:#e8fffb;stroke:rgba(25,240,201,.55);stroke-width:5;transition:opacity .45s}
#flow .dot.dead{display:none}
#flow.focus .dot{opacity:.06}
#flow.focus .dot.hl{opacity:1}
.edge .dot{display:none}

.lite #flow{display:none}
.lite .bg-grid::before,.lite .bg-glow,.lite .scan::after{animation:none}

.tip.pos-br{left:auto;right:16px}
.tip.pos-tl{bottom:auto;top:16px}
.tip.pos-tr{left:auto;right:16px;bottom:auto;top:16px}
