/* WX Flow — styles for the Atlas flow canvas (wxflow.js), drawn as a transit
   map. Loaded beside theme.css; every token here has a fallback so the canvas
   draws correctly on a page that does not carry the theme. The protocol hues
   are written down ONCE, here: one strong colour per protocol line, in the
   house palette, so the two Atlas surfaces and the website figures read as one
   family. Nothing is fetched from anywhere: no font, no image, no CDN.

   Level of detail is a class on the host: .wxflow--estate | --site | --zone.
   Elements carry the class of the LOD they belong to; the rules below fade the
   others out. The marching dashes are driven by one rAF loop in the script and
   stop under prefers-reduced-motion, .wxflow--still or a hidden tab. */

.wxflow{
  --wf-bg:var(--map-bg,#FAFBFC); --wf-grid:rgba(23,32,52,.045);
  --wf-ink:var(--map-node-text,#1A1F24); --wf-sub:var(--map-node-sub,#5C6470);
  --wf-node:var(--map-node,#FFFFFF); --wf-node-line:var(--map-node-line,#C2CAD6);
  --wf-self:var(--map-self,#182547); --wf-self-text:var(--map-self-text,#FFFFFF);
  --wf-sel:var(--map-sel,#006BFF); --wf-navy:#182547; --wf-gold:#EEC139;
  --wf-tile:#FFFFFF; --wf-tile-line:#D6DCE4;
  --wf-zone:rgba(24,37,71,.045); --wf-zone-line:rgba(24,37,71,.12);
  --wf-flow:rgba(255,255,255,.88);
  /* Eight lines, eight colours. HydroFlow purple, DNP3 amber, OPC UA blue,
     EtherNet/IP green, PROFINET teal, Modbus red-brown, MQTT grey-blue. */
  --proto-dnp3:#E0A32E; --proto-hydroflow3:#7C5CD6; --proto-opcua:#2F6FE0; --proto-ethernet_ip:#2E9E5B;
  --proto-profinet:#0E9AA7; --proto-modbus_tcp:#B4553A; --proto-modbus_rtu:#8A6D3B; --proto-mqtt:#5C7C99;
  --proto-unknown:#64748B;
  --state-confirmed:#2E9E5B; --state-declared:#8A94A6; --state-half_open:#D9822B; --state-stale:#A3ABB8; --state-observed:#B8860B; --state-seen:#D2453A;
  --st-online:#2E9E5B; --st-offline:#D2453A; --st-unknown:#A3ABB8;
  position:relative; width:100%; height:100%; min-height:320px; overflow:hidden;
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(0,107,255,.05), transparent 60%),
    linear-gradient(var(--wf-grid) 1px, transparent 1px) 0 0/40px 40px,
    linear-gradient(90deg, var(--wf-grid) 1px, transparent 1px) 0 0/40px 40px,
    var(--wf-bg);
  border-radius:inherit; outline:none; user-select:none; -webkit-user-select:none;
  font-family:var(--sans,'IBM Plex Sans','Segoe UI',system-ui,sans-serif);
}
html[data-theme=dark] .wxflow{
  --wf-bg:#141A22; --wf-grid:rgba(255,255,255,.045); --wf-ink:#E6EAF0; --wf-sub:#9AA4B2;
  --wf-node:#1D2530; --wf-node-line:#39424F; --wf-self:#2B4F86; --wf-tile:#1D2530; --wf-tile-line:#39424F;
  --wf-zone:rgba(127,176,232,.07); --wf-zone-line:rgba(127,176,232,.18);
  --wf-flow:rgba(255,255,255,.80);
  --proto-dnp3:#F2C05A; --proto-hydroflow3:#B39DEB; --proto-opcua:#7FB0E8; --proto-ethernet_ip:#5FD08A;
  --proto-profinet:#4BBBD3; --proto-modbus_tcp:#E08A6E; --proto-modbus_rtu:#C0A06A; --proto-mqtt:#8FAECB;
  --state-confirmed:#5FD08A; --state-declared:#94A3B8; --state-half_open:#F0A35A; --state-stale:#6B7686;
  --state-observed:#E2B94A; --state-seen:#F0776A;
}
.wxflow-svg{position:absolute; inset:0; width:100%; height:100%; display:block; cursor:grab; touch-action:none}
.wxflow--dragging .wxflow-svg{cursor:grabbing}
.wxflow--dragnode .wxflow-svg{cursor:move}
.wxflow text{fill:var(--wf-ink); pointer-events:none}

/* ---- controls -------------------------------------------------------- */
.wxflow-ctl{position:absolute; right:12px; bottom:12px; display:flex; flex-direction:column; gap:4px; z-index:2}
.wxflow-ctl button{width:32px; height:32px; border-radius:8px; border:1px solid var(--wf-tile-line); background:var(--wf-tile);
  color:var(--wf-ink); font:600 16px/1 var(--sans,system-ui); cursor:pointer; box-shadow:0 2px 8px rgba(18,12,91,.10)}
.wxflow-ctl button:hover{border-color:var(--wf-sel); color:var(--wf-sel)}
.wxflow-lodtag{position:absolute; left:12px; top:10px; font-size:11px; font-weight:600; letter-spacing:.14em; text-transform:uppercase;
  color:var(--wf-sub); background:var(--wf-tile); border:1px solid var(--wf-tile-line); border-radius:999px; padding:4px 10px; z-index:2; pointer-events:none}

/* ---- the line key (corner legend) ------------------------------------ */
.wxflow-key{pointer-events:none}
.wxflow-key-bg{fill:var(--wf-tile); stroke:var(--wf-tile-line); stroke-width:1; opacity:.95;
  filter:drop-shadow(0 4px 12px rgba(18,12,91,.10))}
.wxflow-key-title{font-size:11px; font-weight:700; letter-spacing:.16em; fill:var(--wf-sub)}
.wxflow-key-bar{fill:var(--c,var(--proto-unknown))}
.wxflow-key-label{font-size:11px; font-weight:600; letter-spacing:-.01em}
.wxflow-key-count{font-size:11px; fill:var(--wf-sub)}

/* ---- LOD gating ------------------------------------------------------ */
.wxflow .wxflow-tiles, .wxflow .wxflow-exts, .wxflow .wxflow-links--estate, .wxflow .wxflow-zones, .wxflow .wxflow-links--site, .wxflow .wxflow-nodes,
.wxflow .wxflow-link-label, .wxflow .wxflow-station-detail, .wxflow .wxflow-station-labels, .wxflow .wxflow-zone-label,
.wxflow .wxflow-site-title, .wxflow .wxflow-site-ring{
  transition:opacity .28s ease}
.wxflow--estate .wxflow-zones, .wxflow--estate .wxflow-links--site, .wxflow--estate .wxflow-nodes{opacity:0; pointer-events:none}
/* The estate's lines (site to site, site to WX Central, site to an external
   tile) are drawn ONCE and stay on the picture at every level of detail:
   only the tiles give way to the site maps. Zooming never breaks a line. */
.wxflow--site .wxflow-tiles, .wxflow--zone .wxflow-tiles{opacity:0; pointer-events:none}
.wxflow--site .wxflow-link-label, .wxflow--estate .wxflow-link-label{opacity:0}
.wxflow--estate .wxflow-station-labels{opacity:0}
.wxflow--estate .wxflow-station-detail, .wxflow--site .wxflow-station-detail{opacity:0}
.wxflow--zone .wxflow-site-ring, .wxflow--zone .wxflow-site-title{opacity:.35}
/* The estate's external systems are its own STATIONS, so they have a group of
   their own (.wxflow-exts) beside the tiles and are NOT faded with them: they
   stay on the picture at every level of detail. Quieter inside a site, where
   the site's own map is the subject. */
.wxflow--zone .wxflow-exts{opacity:.55}

/* ---- estate tiles ---------------------------------------------------- */
.wxflow-tile{cursor:pointer}
.wxflow-tile-box{fill:var(--wf-tile); stroke:var(--wf-tile-line); stroke-width:1.5; filter:drop-shadow(0 6px 14px rgba(18,12,91,.10)); transition:stroke .15s}
.wxflow-tile:hover .wxflow-tile-box, .wxflow-tile.selected .wxflow-tile-box{stroke:var(--wf-sel); stroke-width:2}
.wxflow-tile.pending .wxflow-tile-box{stroke-dasharray:5 4}
.wxflow-dot{fill:var(--st-unknown)}
.wxflow-tile.online .wxflow-dot{fill:var(--st-online)} .wxflow-tile.offline .wxflow-dot{fill:var(--st-offline)}
.wxflow-tile-name{font-size:14px; font-weight:700; letter-spacing:-.01em}
.wxflow-tile .wxflow-station-outer{stroke-width:2.6}
.wxflow-tile .wxflow-station-inner{stroke-width:1.6}
.wxflow-tile-sub{font-size:12px; fill:var(--wf-sub); letter-spacing:.02em}
/* health in words, beside the dot: never the colour alone */
.wxflow-tile-state{font-size:11.5px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; fill:var(--wf-sub)}
/* the south fan: one row per protocol under the box, and the way in. A card
   is drawn at 0.78 of these sizes on screen at the estate fit, so none is
   under 11.5px: ~9px on screen at the least (fit review, 1 Oct 2026) */
.wxflow-tile-fan{font-size:11.5px; font-weight:600; letter-spacing:.01em; fill:var(--wf-ink)}
.wxflow-tile-fan--note{font-weight:500; fill:var(--wf-sub)}
.wxflow-tile-fan-bar{fill:var(--c,var(--proto-unknown))}
.wxflow-tile-fan-bar.proto-dnp3{--c:var(--proto-dnp3)} .wxflow-tile-fan-bar.proto-hydroflow3{--c:var(--proto-hydroflow3)}
.wxflow-tile-fan-bar.proto-opcua{--c:var(--proto-opcua)} .wxflow-tile-fan-bar.proto-ethernet_ip{--c:var(--proto-ethernet_ip)}
.wxflow-tile-fan-bar.proto-profinet{--c:var(--proto-profinet)} .wxflow-tile-fan-bar.proto-modbus_tcp{--c:var(--proto-modbus_tcp)}
.wxflow-tile-fan-bar.proto-modbus_rtu{--c:var(--proto-modbus_rtu)} .wxflow-tile-fan-bar.proto-mqtt{--c:var(--proto-mqtt)}
.wxflow-tile-open{cursor:pointer}
.wxflow-tile-open-hit{fill:transparent}
.wxflow-tile-open-text{font-size:12px; font-weight:700; fill:var(--wf-sel)}
.wxflow-tile-open:hover .wxflow-tile-open-text{text-decoration:underline}
.wxflow-tile.offline .wxflow-tile-state{fill:var(--st-offline)}
.wxflow-site-state{font-size:12px; font-weight:600; letter-spacing:.08em; text-transform:uppercase; fill:var(--wf-sub)}
.wxflow-site-state.state-offline{fill:var(--st-offline)}
.wxflow-site-state.state-online{fill:var(--st-online)}
/* WX Central: the hub tile at the top of the estate */
.wxflow-hub-tile{fill:var(--wf-self); stroke:var(--wf-gold); stroke-width:2.5; filter:drop-shadow(0 6px 14px rgba(18,12,91,.14))}
.wxflow-ext:hover .wxflow-hub-tile, .wxflow-ext.selected .wxflow-hub-tile, .wxflow-ext.lit .wxflow-hub-tile{stroke:var(--wf-sel); stroke-width:3}
.wxflow-hub .wxflow-ext-name{fill:var(--wf-self-text); font-size:15px}
.wxflow-hub .wxflow-ext-sub{fill:var(--wf-self-text); opacity:.8}

/* ---- terminus stations for external systems --------------------------- */
.wxflow-ext{cursor:pointer}
.wxflow-ext-box{fill:var(--wf-node); stroke:var(--wf-ink); stroke-width:3}
.wxflow-ext:hover .wxflow-ext-box, .wxflow-ext.selected .wxflow-ext-box{stroke:var(--wf-sel)}
.wxflow-ext-name{font-size:14px; font-weight:700}
.wxflow-ext-sub{font-size:12px; fill:var(--wf-sub); letter-spacing:.03em}

/* ---- the site map ----------------------------------------------------- */
.wxflow-site-ring{fill:none; stroke:var(--wf-zone-line); stroke-width:1; stroke-dasharray:2 6}
/* the site's name, as a title inside its own outline's headroom */
.wxflow-site-title{font-size:28px; font-weight:700; letter-spacing:-.02em}
.wxflow-site--empty{cursor:pointer}
.wxflow-site-disc{fill:var(--wf-zone); stroke:var(--wf-zone-line); stroke-dasharray:4 6}
.wxflow-site-hint{font-size:14px; fill:var(--wf-sub)}
/* fare zones: a pale band behind the stations that belong together, drawn as
   the annular sector the zone's arc actually occupies (a bounding box of an
   arc past a half turn swallows the centre and every other zone with it) */
.wxflow-zone-panel{fill:var(--wf-zone); stroke:var(--wf-zone-line); stroke-width:1; stroke-dasharray:6 7}
.wxflow-zone.kind-peers .wxflow-zone-panel{fill:rgba(124,92,214,.06); stroke:rgba(124,92,214,.24)}
.wxflow-zone.kind-upstream .wxflow-zone-panel{fill:rgba(217,130,43,.06); stroke:rgba(217,130,43,.24)}
.wxflow-zone.kind-area .wxflow-zone-panel{fill:rgba(46,158,91,.055); stroke:rgba(46,158,91,.22)}
.wxflow-zone-label{font-size:11px; font-weight:700; letter-spacing:.16em; fill:var(--wf-sub)}

/* ---- stations --------------------------------------------------------- */
.wxflow-station{cursor:pointer}
.wxflow-station-outer{fill:var(--wf-node); stroke:var(--wf-ink); stroke-width:3.4; transition:stroke .15s}
.wxflow-station-inner{fill:none; stroke:var(--wf-ink); stroke-width:2; transition:stroke .15s}
.wxflow-station-outer--self{fill:var(--wf-self); stroke:var(--wf-gold); stroke-width:4}
.wxflow-station-inner--self{stroke:var(--wf-gold); stroke-width:2}
.wxflow-station-tick{stroke:var(--wf-ink); stroke-width:5; stroke-linecap:round; transition:stroke .15s}
.wxflow-station-hit{fill:transparent}
.wxflow-station-terminus{fill:var(--wf-node); stroke:var(--wf-ink); stroke-width:3.4; transition:stroke .15s}
.wxflow-station:hover .wxflow-station-outer, .wxflow-station.lit .wxflow-station-outer, .wxflow-station.selected .wxflow-station-outer,
.wxflow-station:hover .wxflow-station-tick, .wxflow-station.lit .wxflow-station-tick, .wxflow-station.selected .wxflow-station-tick,
.wxflow-station:hover .wxflow-station-terminus, .wxflow-station.lit .wxflow-station-terminus, .wxflow-station.selected .wxflow-station-terminus{stroke:var(--wf-sel)}
.wxflow-station-label{font-size:13px; font-weight:600; letter-spacing:-.01em}
.wxflow-station-label--major{font-size:15px; font-weight:700}
.wxflow-station-sub{font-size:11px; fill:var(--wf-sub)}
.wxflow-station-role{font-size:11px; fill:var(--wf-sub); letter-spacing:.14em}
.wxflow-self-glow{fill:var(--wf-gold); opacity:.20; filter:blur(10px)}
.wxflow .wxflow-self-a{font-size:15px; font-weight:700; fill:var(--wf-self-text); letter-spacing:.06em}

/* ---- the lines -------------------------------------------------------- */
.wxflow-link{--c:var(--proto-unknown)}
.wxflow-link.proto-dnp3, .wxflow-key-bar.proto-dnp3{--c:var(--proto-dnp3)}
.wxflow-link.proto-hydroflow3, .wxflow-key-bar.proto-hydroflow3{--c:var(--proto-hydroflow3)}
.wxflow-link.proto-opcua, .wxflow-key-bar.proto-opcua{--c:var(--proto-opcua)}
.wxflow-link.proto-ethernet_ip, .wxflow-key-bar.proto-ethernet_ip{--c:var(--proto-ethernet_ip)}
.wxflow-link.proto-profinet, .wxflow-key-bar.proto-profinet{--c:var(--proto-profinet)}
.wxflow-link.proto-modbus_tcp, .wxflow-key-bar.proto-modbus_tcp{--c:var(--proto-modbus_tcp)}
.wxflow-link.proto-modbus_rtu, .wxflow-key-bar.proto-modbus_rtu{--c:var(--proto-modbus_rtu)}
.wxflow-link.proto-mqtt, .wxflow-key-bar.proto-mqtt{--c:var(--proto-mqtt)}
.wxflow-link.proto-unknown, .wxflow-key-bar.proto-unknown{--c:var(--proto-unknown)}
/* half-open and stale override the protocol hue: the fault is the message */
.wxflow-link.state-half_open{--c:var(--state-half_open)}
.wxflow-link.state-stale{--c:var(--state-stale)}
.wxflow-link.seen{--c:var(--state-seen)}
.wxflow-link-halo{fill:none; stroke:var(--c); stroke-width:14; stroke-linejoin:round; stroke-linecap:round; opacity:.10}
.wxflow-link-core{fill:none; stroke:var(--c); stroke-width:7; stroke-linejoin:round; stroke-linecap:round; opacity:.96}
.wxflow-link-cap{fill:none; stroke:var(--c); stroke-width:5; stroke-linecap:round}
.wxflow-link-hit{fill:none; stroke:transparent; stroke-width:20; cursor:pointer; pointer-events:stroke}
/* state on the line */
.wxflow-link.state-confirmed .wxflow-link-core{stroke-width:7}
.wxflow-link.state-declared .wxflow-link-core{stroke-width:4; opacity:.72}
.wxflow-link.state-observed .wxflow-link-core{stroke-width:5.5; stroke-dasharray:0.1 12}
.wxflow-link.state-half_open .wxflow-link-core{stroke-width:7}
.wxflow-link.state-stale .wxflow-link-core{stroke-width:4.5; opacity:.7}
.wxflow-link.seen .wxflow-link-core{stroke-dasharray:3 8}
.wxflow-link.silent .wxflow-link-halo{opacity:.16}
/* the flow: a marching dash riding the line, driven by the script's rAF loop */
.wxflow-link-flow{fill:none; stroke:var(--wf-flow); stroke-width:3.2; stroke-linecap:round; pointer-events:none}
/* on a dotted (observed) line the dash is the line's own colour, so it reads as
   one solid run of traffic travelling along it rather than a gap of white */
.wxflow-link.state-observed .wxflow-link-flow{stroke:var(--c); stroke-width:5.5}
.wxflow--estate .wxflow-link-halo{stroke-width:26}
.wxflow--estate .wxflow-link-core{stroke-width:13}
.wxflow--estate .wxflow-link-flow{stroke-width:6}
.wxflow--still .wxflow-link-flow{display:none}
@media (prefers-reduced-motion:reduce){
  .wxflow .wxflow-link-flow{display:none}
  .wxflow *{transition:none !important}
}
.wxflow-link.lit .wxflow-link-core{stroke-width:9} .wxflow-link.lit .wxflow-link-halo{opacity:.30}
.wxflow-link.selected .wxflow-link-core{stroke-width:9; filter:drop-shadow(0 0 5px var(--c))}
/* a trace: the lit run stays, everything else steps back - including the
   tiles and the external systems on the estate, since a run can cross sites */
.wxflow--focus .wxflow-link:not(.lit):not(.lit-via){opacity:.22}
.wxflow--focus .wxflow-station:not(.lit):not(.lit-via){opacity:.42}
.wxflow--focus .wxflow-tile:not(.lit):not(.lit-via), .wxflow--focus .wxflow-ext:not(.lit):not(.lit-via){opacity:.45}
/* the second way round, by way of WX Central: dashed, so it reads as the
   alternative and never as the run itself */
.wxflow-link.lit-via:not(.lit) .wxflow-link-core{stroke-dasharray:14 10; stroke-width:7; opacity:.9}
.wxflow-link.lit-via:not(.lit) .wxflow-link-halo{opacity:.18}
.wxflow-tile.lit .wxflow-tile-box, .wxflow-ext.lit .wxflow-ext-box{stroke:var(--wf-sel); stroke-width:2.5}
.wxflow-arrow path{fill:var(--proto-unknown)}
.wxflow-arrow.dnp3 path{fill:var(--proto-dnp3)} .wxflow-arrow.hydroflow3 path{fill:var(--proto-hydroflow3)}
.wxflow-arrow.opcua path{fill:var(--proto-opcua)} .wxflow-arrow.ethernet_ip path{fill:var(--proto-ethernet_ip)}
.wxflow-arrow.profinet path{fill:var(--proto-profinet)} .wxflow-arrow.modbus_tcp path{fill:var(--proto-modbus_tcp)}
.wxflow-arrow.modbus_rtu path{fill:var(--proto-modbus_rtu)} .wxflow-arrow.mqtt path{fill:var(--proto-mqtt)}
.wxflow-arrow.half_open path{fill:var(--state-half_open)} .wxflow-arrow.stale path{fill:var(--state-stale)}
.wxflow-arrow.declared path{fill:var(--state-declared)} .wxflow-arrow.observed path{fill:var(--state-observed)} .wxflow-arrow.seen path{fill:var(--state-seen)}
.wxflow-link-label-bg{fill:var(--wf-tile); stroke:var(--c); stroke-width:1.2; opacity:.97}
.wxflow-link-label-text{font-size:11px; font-weight:600; letter-spacing:.02em; fill:var(--wf-ink)}
/* the state IN WORDS on a line that is not simply confirmed: shown at the
   site level, where the fuller pill above is hidden; the pill takes over at
   the zone level and says the same thing, so the two are never both drawn */
.wxflow-link-word{transition:opacity .28s ease}
/* a word that could not sit on its line stands beside it, joined by a short leader */
.wxflow-link-leader{stroke:var(--c); stroke-width:1.4; fill:none; opacity:.9}
.wxflow-link-word-bg{fill:var(--wf-tile); stroke:var(--c); stroke-width:1; opacity:.96}
.wxflow-link-word-text{font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; fill:var(--c); text-anchor:middle}
/* where a line that runs out of the window goes: a quiet tag on it by the window's edge */
.wxflow-link-exit-bg{fill:var(--wf-tile); stroke:var(--c); stroke-width:1; stroke-dasharray:3 2}
.wxflow-link-exit-text{font-size:11px; font-weight:700; letter-spacing:0; fill:var(--wf-ink); text-anchor:middle}
.wxflow--zone .wxflow-link-word, .wxflow--estate .wxflow-links--site .wxflow-link-word{opacity:0}
.wxflow--estate .wxflow-link-word-bg{stroke-width:1.6}

/* ---- the compass: dense blocks and the narrow layout ------------------- */
/* a block of many devices on one protocol says how many at the site level
   and shows the names only when the reader zooms into the zone */
.wxflow-lod-pill{transition:opacity .28s ease}
.wxflow-lod-pill-bg{fill:var(--wf-tile); stroke:var(--wf-ink); stroke-width:1.4}
.wxflow-lod-pill-text{font-size:11px; font-weight:700; letter-spacing:.04em}
.wxflow--zone .wxflow-lod-pill, .wxflow--estate .wxflow-lod-pill{opacity:0}
.wxflow--site .wxflow-station.dense .wxflow-station-labels{opacity:0}
/* a phone: the controls sit in the corner, smaller, and the tag steps aside */
@media (max-width:640px){
  .wxflow-ctl{right:8px; bottom:8px; gap:3px}
  .wxflow-ctl button{width:36px; height:36px}
  .wxflow-lodtag{left:8px; top:8px; font-size:11px; padding:3px 8px}
}
