Files
bztmon-site/src/components/Constellation.astro
T
2026-08-17 23:21:38 +10:00

267 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
// The fleet constellation - 6 real nodes as IC chips on PCB traces.
// Pure SVG + scoped CSS keyframes (zero JS; reduced-motion turns every animation off).
// Anatomy per the design handoff: each route is TWO parallel traces (offset ±(2,-3)) -
// one carries a cyan packet outbound, the other a magenta packet inbound. Packets are
// stroke-dasharray 9/127 sliding via stroke-dashoffset. Hub = cave (56px chip, radar ring).
---
<svg
class="constellation"
viewBox="0 0 760 640"
role="img"
aria-label="Live topology of the six-node homelab fleet: hub cave reconciling alfred, robin, kate, wgirl and elfastc"
>
<defs>
<linearGradient id="lgA" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="var(--accent)"></stop>
<stop offset="1" stop-color="var(--accent-2)"></stop>
</linearGradient>
</defs>
<!-- ── PCB traces: dual parallel lines per route (out = cyan, back = magenta) ── -->
<!-- alfred -->
<g transform="translate(2,-3)">
<path class="t-glow t-cyan" d="M380 310 H460 L520 250 V180 L572 128 H595"></path>
<path class="t-base t-cyan" d="M380 310 H460 L520 250 V180 L572 128 H595"></path>
<path class="t-pkt pkt-fwd" d="M380 310 H460 L520 250 V180 L572 128 H595"></path>
</g>
<g transform="translate(-2,3)">
<path class="t-glow t-mag" d="M380 310 H460 L520 250 V180 L572 128 H595"></path>
<path class="t-base t-mag" d="M380 310 H460 L520 250 V180 L572 128 H595"></path>
<path class="t-pkt t-pkt--mag pkt-rev-slow" d="M380 310 H460 L520 250 V180 L572 128 H595"></path>
</g>
<!-- robin -->
<g transform="translate(2,-3)">
<path class="t-glow t-cyan" d="M380 310 H470 L540 380 H602 L640 420"></path>
<path class="t-base t-cyan" d="M380 310 H470 L540 380 H602 L640 420"></path>
<path class="t-pkt pkt-fwd-slow" d="M380 310 H470 L540 380 H602 L640 420"></path>
</g>
<g transform="translate(-2,3)">
<path class="t-glow t-mag" d="M380 310 H470 L540 380 H602 L640 420"></path>
<path class="t-base t-mag" d="M380 310 H470 L540 380 H602 L640 420"></path>
<path class="t-pkt t-pkt--mag pkt-rev" d="M380 310 H470 L540 380 H602 L640 420"></path>
</g>
<!-- kate (standby - dimmer) -->
<g transform="translate(2,-3)" class="standby">
<path class="t-glow t-cyan" d="M380 310 H300 L240 250 V202 L188 150 H176"></path>
<path class="t-base t-cyan" d="M380 310 H300 L240 250 V202 L188 150 H176"></path>
<path class="t-pkt t-pkt--solid pkt-fwd-slow" d="M380 310 H300 L240 250 V202 L188 150 H176"></path>
</g>
<g transform="translate(-2,3)" class="standby">
<path class="t-glow t-mag" d="M380 310 H300 L240 250 V202 L188 150 H176"></path>
<path class="t-base t-mag" d="M380 310 H300 L240 250 V202 L188 150 H176"></path>
<path class="t-pkt t-pkt--mag t-pkt--solid pkt-rev" d="M380 310 H300 L240 250 V202 L188 150 H176"></path>
</g>
<!-- wgirl (standby) -->
<g transform="translate(2,-3)" class="standby">
<path class="t-glow t-cyan" d="M380 310 H310 L244 376 V446 L214 474"></path>
<path class="t-base t-cyan" d="M380 310 H310 L244 376 V446 L214 474"></path>
<path class="t-pkt t-pkt--solid pkt-fwd" d="M380 310 H310 L244 376 V446 L214 474"></path>
</g>
<g transform="translate(-2,3)" class="standby">
<path class="t-glow t-mag" d="M380 310 H310 L244 376 V446 L214 474"></path>
<path class="t-base t-mag" d="M380 310 H310 L244 376 V446 L214 474"></path>
<path class="t-pkt t-pkt--mag t-pkt--solid pkt-rev-slow" d="M380 310 H310 L244 376 V446 L214 474"></path>
</g>
<!-- elfastc (standby) -->
<g transform="translate(3,2)" class="standby">
<path class="t-glow t-cyan" d="M380 310 V400 L452 472 V532"></path>
<path class="t-base t-cyan" d="M380 310 V400 L452 472 V532"></path>
<path class="t-pkt t-pkt--solid pkt-fwd-slow" d="M380 310 V400 L452 472 V532"></path>
</g>
<g transform="translate(-3,-2)" class="standby">
<path class="t-glow t-mag" d="M380 310 V400 L452 472 V532"></path>
<path class="t-base t-mag" d="M380 310 V400 L452 472 V532"></path>
<path class="t-pkt t-pkt--mag t-pkt--solid pkt-rev" d="M380 310 V400 L452 472 V532"></path>
</g>
<!-- vias at trace bends -->
<circle class="via via--cyan" cx="460" cy="310" r="3"></circle>
<circle class="via via--cyan" cx="520" cy="250" r="3"></circle>
<circle class="via via--cyan" cx="520" cy="180" r="3"></circle>
<circle class="via via--mag" cx="470" cy="310" r="3"></circle>
<circle class="via via--mag" cx="540" cy="380" r="3"></circle>
<circle class="via via--dim" cx="300" cy="310" r="3"></circle>
<circle class="via via--dim" cx="240" cy="250" r="3"></circle>
<circle class="via via--dim" cx="310" cy="310" r="3"></circle>
<circle class="via via--dim" cx="244" cy="376" r="3"></circle>
<circle class="via via--dim" cx="380" cy="400" r="3"></circle>
<circle class="via via--dim" cx="452" cy="472" r="3"></circle>
<!-- ── hub: cave ── -->
<circle class="ring" cx="380" cy="310" r="46" fill="none" stroke="url(#lgA)" stroke-width="1.5"></circle>
<circle cx="380" cy="310" r="46" fill="rgba(63,186,245,0.05)" stroke="rgba(63,186,245,0.3)" stroke-width="1"></circle>
<!-- pin stubs -->
<g class="pins-cyan">
<line x1="352" y1="296" x2="340" y2="296"></line><line x1="352" y1="310" x2="340" y2="310"></line>
<line x1="352" y1="324" x2="340" y2="324"></line><line x1="366" y1="282" x2="366" y2="270"></line>
<line x1="380" y1="282" x2="380" y2="270"></line><line x1="366" y1="338" x2="366" y2="350"></line>
<line x1="380" y1="338" x2="380" y2="350"></line>
</g>
<g class="pins-mag">
<line x1="408" y1="296" x2="420" y2="296"></line><line x1="408" y1="310" x2="420" y2="310"></line>
<line x1="408" y1="324" x2="420" y2="324"></line><line x1="394" y1="282" x2="394" y2="270"></line>
<line x1="394" y1="338" x2="394" y2="350"></line>
</g>
<rect class="chip chip--hub" x="352" y="282" width="56" height="56" rx="8" stroke="url(#lgA)"></rect>
<rect x="364" y="294" width="32" height="32" rx="4" fill="none" stroke="rgba(63,186,245,0.4)" stroke-width="1"></rect>
<circle class="core core--cyan pulse" cx="380" cy="310" r="5"></circle>
<rect class="plate plate--hub" x="338" y="352" width="84" height="24" rx="4"></rect>
<text class="lbl lbl--hub" x="380" y="368" text-anchor="middle">cave</text>
<!-- ── awake: alfred (Intel compute - NUC iGPU/QuickSync) ── -->
<circle cx="612" cy="128" r="24" fill="rgba(0,199,253,0.07)"></circle>
<rect class="chip chip--intel" x="595" y="111" width="34" height="34" rx="5"></rect>
<circle class="core core--intel pulse" cx="612" cy="128" r="4"></circle>
<rect class="plate plate--intel" x="576" y="152" width="72" height="20" rx="3"></rect>
<text class="lbl" x="612" y="166" text-anchor="middle">alfred</text>
<!-- ── awake: robin ── -->
<circle cx="656" cy="420" r="24" fill="rgba(232,121,249,0.07)"></circle>
<rect class="chip chip--mag" x="639" y="403" width="34" height="34" rx="5"></rect>
<circle class="core core--mag pulse-slow" cx="656" cy="420" r="4"></circle>
<rect class="plate plate--mag" x="620" y="444" width="72" height="20" rx="3"></rect>
<text class="lbl" x="656" y="458" text-anchor="middle">robin</text>
<!-- ── standby: kate ── -->
<rect class="chip chip--cyan" x="145" y="135" width="30" height="30" rx="5"></rect>
<circle class="core core--cyan pulse-slow" cx="160" cy="150" r="3.5"></circle>
<rect class="plate plate--dim" x="126" y="172" width="68" height="20" rx="3"></rect>
<text class="lbl lbl--dim" x="160" y="186" text-anchor="middle">kate</text>
<!-- ── standby: wgirl ── -->
<rect class="chip chip--mag" x="199" y="471" width="30" height="30" rx="5"></rect>
<circle class="core core--mag pulse" cx="214" cy="486" r="3.5"></circle>
<rect class="plate plate--dim" x="180" y="508" width="68" height="20" rx="3"></rect>
<text class="lbl lbl--dim" x="214" y="522" text-anchor="middle">wgirl</text>
<!-- ── standby: elfastc (Intel compute - 2× Arc B580) ── -->
<rect class="chip chip--intel" x="437" y="533" width="30" height="30" rx="5"></rect>
<circle class="core core--intel pulse-slow" cx="452" cy="548" r="3.5"></circle>
<rect class="plate plate--dim" x="412" y="570" width="80" height="20" rx="3"></rect>
<text class="lbl lbl--dim" x="452" y="584" text-anchor="middle">elfastc</text>
<!-- ── key: compute colours (right side - left edge clips on narrow heroes) ── -->
<g aria-label="compute colour key">
<rect class="core--mag" x="620" y="572" width="8" height="8" rx="2"></rect>
<text class="cap cap--key" x="634" y="580">nvidia compute</text>
<rect class="core--intel" x="620" y="592" width="8" height="8" rx="2"></rect>
<text class="cap cap--key" x="634" y="600">intel compute</text>
<rect class="key--bat" x="620" y="612" width="8" height="8" rx="2"></rect>
<text class="cap cap--key" x="634" y="620">core / services</text>
</g>
</svg>
<style>
.constellation {
display: block;
width: 100%;
height: auto;
}
/* trace layers */
.t-glow { fill: none; stroke-width: 7; }
.t-glow.t-cyan { stroke: rgba(63, 186, 245, 0.12); }
.t-glow.t-mag { stroke: rgba(232, 121, 249, 0.1); }
.standby .t-glow.t-cyan { stroke: rgba(63, 186, 245, 0.1); }
.standby .t-glow.t-mag { stroke: rgba(232, 121, 249, 0.08); }
.t-base { fill: none; stroke-width: 2; }
.t-base.t-cyan { stroke: rgba(63, 186, 245, 0.45); }
.t-base.t-mag { stroke: rgba(232, 121, 249, 0.38); }
.standby .t-base.t-cyan { stroke: rgba(63, 186, 245, 0.35); }
.standby .t-base.t-mag { stroke: rgba(232, 121, 249, 0.3); }
/* the moving packets */
.t-pkt {
fill: none;
stroke: url(#lgA);
stroke-width: 3;
stroke-linecap: round;
stroke-dasharray: 9 127;
filter: drop-shadow(0 0 6px rgba(63, 186, 245, 0.9));
}
.t-pkt--mag { filter: drop-shadow(0 0 6px rgba(232, 121, 249, 0.9)); }
.t-pkt--solid { stroke: rgba(63, 186, 245, 0.85); }
.t-pkt--solid.t-pkt--mag { stroke: rgba(232, 121, 249, 0.85); }
.pkt-fwd { animation: bzDash 8s linear infinite; }
.pkt-fwd-slow { animation: bzDash 11s linear infinite; }
.pkt-rev { animation: bzDashRev 9s linear infinite; }
.pkt-rev-slow { animation: bzDashRev 13s linear infinite; }
/* vias */
.via { fill: var(--panel-solid); stroke-width: 1; }
.via--cyan { stroke: rgba(63, 186, 245, 0.6); }
.via--mag { stroke: rgba(232, 121, 249, 0.6); }
.via--dim { stroke: rgba(126, 139, 161, 0.5); }
/* chips + cores + plates + labels */
.chip { fill: var(--panel-solid); }
.chip--hub { stroke-width: 2; filter: drop-shadow(0 0 10px rgba(63, 186, 245, 0.45)); }
.chip--cyan { stroke: var(--accent); stroke-width: 1.5; filter: drop-shadow(0 0 8px rgba(63, 186, 245, 0.55)); }
.chip--mag { stroke: var(--accent-2); stroke-width: 1.5; filter: drop-shadow(0 0 8px rgba(232, 121, 249, 0.55)); }
.chip--intel { stroke: var(--accent-intel); stroke-width: 1.5; filter: drop-shadow(0 0 8px rgba(0, 199, 253, 0.55)); }
.core--cyan { fill: var(--accent); filter: drop-shadow(0 0 6px rgba(63, 186, 245, 1)); }
.core--mag { fill: var(--accent-2); filter: drop-shadow(0 0 5px rgba(232, 121, 249, 1)); }
.core--intel { fill: var(--accent-intel); filter: drop-shadow(0 0 6px rgba(0, 199, 253, 1)); }
.pulse { animation: bzPulse 2.4s ease-in-out infinite; }
.pulse-slow { animation: bzPulse 3.4s ease-in-out infinite; }
.pins-cyan line { stroke: rgba(63, 186, 245, 0.6); stroke-width: 1.5; }
.pins-mag line { stroke: rgba(232, 121, 249, 0.6); stroke-width: 1.5; }
.plate { fill: rgba(13, 19, 34, 0.92); stroke-width: 1; }
.plate--hub { stroke: rgba(63, 186, 245, 0.4); }
.plate--cyan { stroke: rgba(63, 186, 245, 0.35); }
.plate--mag { stroke: rgba(232, 121, 249, 0.35); }
.plate--intel { stroke: rgba(0, 199, 253, 0.35); }
.plate--dim { stroke: rgba(126, 139, 161, 0.3); }
.lbl {
fill: #e8f2fb;
font-family: var(--font-mono);
font-size: 11px;
}
.lbl--hub { font-size: 12px; font-weight: 700; }
.lbl--dim { fill: #9fb0c8; }
.cap { font-family: var(--font-mono); }
.cap--hub {
fill: var(--accent);
font-size: 9.5px;
filter: drop-shadow(0 0 4px rgba(63, 186, 245, 0.7));
}
.cap--dim { fill: #5c6a82; font-size: 9px; }
.cap--key { fill: #7e8ba1; font-size: 9px; }
/* gloss bat-black with a faint gold ring - the batcave swatch for core/services */
.key--bat {
fill: #10131c;
stroke: rgba(245, 197, 24, 0.55);
stroke-width: 1;
filter: drop-shadow(0 0 4px rgba(245, 197, 24, 0.35));
}
/* radar ring off the hub */
.ring {
transform-box: fill-box;
transform-origin: center;
animation: bzRing 3.6s ease-out infinite;
}
/* dash pattern is 9+127 = a 136px period - the loop offset MUST be an exact multiple of it
(2×136 = 272) or every packet dot visibly teleports at the loop seam. */
@keyframes bzDash { to { stroke-dashoffset: -272; } }
@keyframes bzDashRev { to { stroke-dashoffset: 272; } }
@keyframes bzPulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
@keyframes bzRing {
/* start AND end at opacity 0 so the 3.6s restart never pops */
0% { transform: scale(0.5); opacity: 0; }
10% { opacity: 0.9; }
100% { transform: scale(1.7); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.pkt-fwd, .pkt-fwd-slow, .pkt-rev, .pkt-rev-slow, .pulse, .pulse-slow, .ring {
animation: none;
}
.ring { opacity: 0.5; }
}
</style>