@font-face{font-family:Montserrat;font-display:swap;src:url('/fonts/Montserrat-Regular.ttf')}@font-face{font-family:Montserrat;font-display:swap;src:url('/fonts/Montserrat-Bold.ttf');font-weight:700}
*{box-sizing:border-box}body{margin:0;background:var(--color-bg);color:var(--color-text);font:14px Montserrat,system-ui,sans-serif}a{color:var(--color-info-text)}h1{font-size:28px;margin:8px 0}h2{font-size:17px}p{line-height:1.6;color:var(--color-text-muted)}button,input{font:inherit}button{cursor:pointer;background:var(--color-primary);color:var(--color-on-primary);border:1px solid transparent;border-radius:8px;padding:12px 18px}button:focus-visible,a:focus-visible,input:focus-visible{outline:3px solid var(--color-primary-text);outline-offset:3px}.shell{display:grid;grid-template-columns:220px minmax(0,1fr);min-height:100vh}aside{background:var(--color-surface);color:var(--color-text);border-right:1px solid var(--color-border);padding:32px 20px;display:flex;flex-direction:column}aside p{color:var(--color-text-muted)}.brand{font-size:25px;font-weight:700}.brand span{color:var(--color-primary-text)}.eyebrow{font-size:10px;letter-spacing:2px;font-weight:700}nav{display:grid;gap:8px;margin-top:40px}nav button{text-align:left;background:transparent;color:var(--color-text-muted);border-left:3px solid transparent}nav button.selected{background:var(--color-primary-subtle);color:var(--color-primary-text);border-left-color:var(--color-primary);font-weight:700}.aside-foot{margin-top:auto;padding-top:45px}main{padding:32px;min-width:0}header{display:flex;justify-content:space-between;align-items:center;gap:20px}header p{margin:6px 0 20px}.notice{border-left:4px solid var(--color-info-text);background:var(--color-primary-subtle);padding:16px 20px;line-height:1.6;margin:15px 0 26px;border-radius:6px}.flow{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:18px}.stage,.panel{background:var(--color-surface);border:1px solid var(--color-border);border-radius:12px;padding:24px}.stage{border-top:3px solid var(--color-border)}.stage.error{border-top-color:var(--color-danger-text)}.stage.stale{border-top-color:var(--color-warning-text)}.stage-top{display:flex;align-items:center;gap:8px;color:var(--color-text-muted);text-transform:uppercase;font-size:10px;letter-spacing:1px}.dot{width:8px;height:8px;background:var(--color-text-muted);border-radius:50%}.active .dot,.idle .dot{background:var(--color-success-text)}.error .dot{background:var(--color-danger-text)}.stale .dot{background:var(--color-warning-text)}.number{font-size:36px;font-variant-numeric:tabular-nums;display:block;margin:20px 0 0}.metric{display:flex;justify-content:space-between;border-top:1px solid var(--color-border);padding:12px 0;font-size:12px}.stage small{display:block;margin-top:14px;color:var(--color-text-muted)}.two-col{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:24px}.bar-row{display:grid;grid-template-columns:150px 1fr 55px;gap:12px;align-items:center;margin:22px 0;font-size:12px}meter{width:100%;height:18px}.good{color:var(--color-success-text)}.footnote,footer{font-size:11px;color:var(--color-text-muted);line-height:1.7;margin-top:25px}.table-scroll{overflow:auto}table{border-collapse:collapse;width:100%;text-align:left}th,td{padding:14px;border-bottom:1px solid var(--color-border);font-size:12px;vertical-align:top}.value{display:inline-block;background:var(--color-primary-subtle);padding:7px;border-radius:5px;margin:3px;max-width:320px;overflow-wrap:anywhere}.failure{border-top:1px solid var(--color-border);padding:16px 0}.badge{font-size:11px;color:var(--color-warning-text)}dl{display:grid;grid-template-columns:150px 1fr;gap:18px}dd{margin:0}.login{max-width:480px;margin:8vh auto;background:var(--color-surface);border:1px solid var(--color-border);border-radius:18px;padding:40px}.login form{display:grid;gap:12px;margin:30px 0}.login input{border:1px solid var(--color-border);background:var(--color-bg);color:var(--color-text);padding:13px;border-radius:7px}.error{color:var(--color-danger-text)}@media(max-width:900px){.shell{grid-template-columns:1fr}aside{padding:20px}nav{display:flex;flex-wrap:wrap;margin-top:15px}.aside-foot{display:none}.two-col{grid-template-columns:1fr}main{padding:20px}header{align-items:start}header h1{font-size:25px}}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}

.flow-map{margin-bottom:24px;overflow:hidden}.flow-map-heading{display:flex;justify-content:space-between;align-items:center;gap:16px}.flow-map-heading h2{margin:8px 0}.flow-map-heading .eyebrow{color:var(--color-text-muted);margin:0}.flow-map-heading button{background:var(--color-primary-subtle);color:var(--color-text);border:1px solid var(--color-border)}.flow-lane{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));list-style:none;margin:30px 0 24px;padding:0;gap:30px}.flow-stop{position:relative;display:flex;flex-direction:column;align-items:center;text-align:center;min-width:0}.flow-zone{font-size:9px;letter-spacing:1px;text-transform:uppercase;color:var(--color-text-muted);min-height:25px}.flow-device{width:88px;height:76px;padding:8px;border:1px solid var(--color-border);border-radius:16px;background:var(--color-bg);color:var(--color-text-muted);display:grid;place-items:center;margin:4px 0 15px}.flow-device svg{width:100%;height:100%}.flow-stop.moving .flow-device{color:var(--color-info-text);border-color:var(--color-info-text);background:var(--color-primary-subtle)}.flow-stop.boundary .flow-device{color:var(--color-primary-text);border-color:var(--color-primary-text)}.flow-stop.error .flow-device{color:var(--color-danger-text);border-color:var(--color-danger-text)}.flow-stop.stale .flow-device{color:var(--color-warning-text);border-color:var(--color-warning-text)}.flow-stop strong{font-size:13px}.flow-detail{font-size:10px;color:var(--color-text-muted);line-height:1.7;margin:7px 0;min-height:34px}.flow-observation{font-size:10px;line-height:1.5;color:var(--color-text-muted)}.flow-stop.moving .flow-observation{color:var(--color-info-text)}.flow-stop.stale .flow-observation,.flow-stop.waiting .flow-observation{color:var(--color-warning-text)}.flow-stop.error .flow-observation{color:var(--color-danger-text)}.flow-connector{position:absolute;top:66px;left:calc(50% + 47px);width:calc(100% - 64px);height:18px;color:var(--color-border);transform:translateY(-50%)}.flow-rail{position:absolute;left:0;right:0;top:8px;border-top:2px dashed currentColor}.flow-arrow{position:absolute;right:-5px;top:-2px;font-size:20px;line-height:20px}.flow-connector.moving{color:var(--color-info-text)}.flow-connector.moving .flow-rail{border-top-style:solid;opacity:.35}.flow-connector.stale,.flow-connector.waiting{color:var(--color-warning-text)}.flow-connector.error{color:var(--color-danger-text)}.flow-packet{display:none;position:absolute;left:0;top:5px;width:8px;height:8px;background:currentColor;border-radius:50%}.moving>.flow-packet{display:block;animation:flow-travel 1.6s linear infinite}.motion-paused .flow-packet{animation-play-state:paused}.flow-map-legend{display:flex;flex-wrap:wrap;gap:16px;border-top:1px solid var(--color-border);padding-top:17px;font-size:10px;color:var(--color-text-muted)}.flow-map-legend>span{display:flex;align-items:center;gap:7px}.flow-key{width:16px;border-top:2px solid var(--color-info-text)}.flow-key.unknown{border-color:var(--color-text-muted);border-top-style:dashed}.flow-key.stale{border-color:var(--color-warning-text)}.flow-map-note{font-size:11px;line-height:1.7;margin-bottom:0}@keyframes flow-travel{from{left:0;opacity:0}15%{opacity:1}85%{opacity:1}to{left:calc(100% - 6px);opacity:0}}@media(max-width:1100px) and (min-width:761px){.flow-lane{gap:16px}.flow-connector{width:calc(100% - 78px)}}@media(max-width:760px){.flow-map-heading{align-items:start;flex-direction:column}.flow-map-heading button{width:100%}.flow-lane{grid-template-columns:1fr;gap:40px;margin-top:24px}.flow-stop{display:grid;grid-template-columns:76px minmax(0,1fr);text-align:left;align-items:start;column-gap:18px}.flow-device{grid-column:1;grid-row:1/5;width:76px;height:72px;margin:0}.flow-zone,.flow-stop strong,.flow-detail,.flow-observation{grid-column:2}.flow-zone{min-height:0;font-size:9px;margin-bottom:8px}.flow-detail{min-height:0;margin:5px 0}.flow-connector{left:38px;top:calc(100% + 6px);width:1px;height:28px;transform:none}.flow-rail{left:0;top:0;bottom:0;border-top:0;border-left:2px dashed currentColor}.flow-connector.moving .flow-rail{border-left-style:solid}.flow-arrow{left:-7px;top:auto;bottom:-5px;right:auto;transform:rotate(90deg)}.flow-packet{left:-3px;top:0}.moving>.flow-packet{animation-name:flow-travel-down}.flow-map-legend{gap:12px}}@keyframes flow-travel-down{from{top:0;opacity:0}15%{opacity:1}85%{opacity:1}to{top:22px;opacity:0}}@media(prefers-reduced-motion:reduce){.flow-packet{animation:none!important}}

.health-badge{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:700;margin:3px 0 8px;color:var(--color-text-muted)}.health-light{width:8px;height:8px;border-radius:50%;background:currentColor;flex:none}.health-badge.online{color:var(--color-success-text)}.health-badge.stale{color:var(--color-warning-text)}.health-badge.fault{color:var(--color-danger-text)}.flow-stop.health-online .flow-device{color:var(--color-success-text);border-color:var(--color-success-text)}.flow-stop.health-fault .flow-device{color:var(--color-danger-text);border-color:var(--color-danger-text)}.flow-stop.health-stale .flow-device{color:var(--color-warning-text);border-color:var(--color-warning-text)}@media(max-width:760px){.health-badge{grid-column:2}.flow-device{grid-row:1/6}}

/* Shared appliance shell, using the same semantic contract as the main UteamUP app. */
button:hover{background:var(--color-primary-hover)}
button:disabled{cursor:wait;opacity:.6}
nav button:hover{background:var(--color-surface-muted);color:var(--color-text)}
header button,.flow-map-heading button{background:var(--color-surface);color:var(--color-text);border:1px solid var(--color-border)}
header button:hover,.flow-map-heading button:hover{background:var(--color-surface-muted)}
.aside-foot{border-top:1px solid var(--color-border);padding-top:24px;margin-top:auto}
.appliance-label{display:inline-flex;align-items:center;gap:8px;color:var(--color-primary-text);font-weight:700;font-size:12px}
.appliance-label svg{width:24px;height:24px}
.overview-context{display:flex;justify-content:space-between;align-items:center;gap:24px;margin:0 0 24px;padding:20px 24px;background:var(--color-surface);border:1px solid var(--color-border);border-radius:12px}
.overview-context h2{margin:0 0 6px}.overview-context p{margin:0;max-width:680px;font-size:12px}
.local-health{display:flex;gap:20px;flex-wrap:wrap;flex-shrink:0}.local-health>div{display:flex;flex-direction:column;gap:4px}.local-health .health-badge{margin:0}
.local-health small{font-size:11px;color:var(--color-text-muted)}
.theme-section-title{color:var(--color-text);font-weight:700}
.notice{font-size:12px;background:var(--color-surface);border-left-color:var(--color-primary)}
.flow-map-heading .eyebrow{color:var(--color-primary-text)}
.flow-map-note{max-width:100ch}
meter{accent-color:var(--color-primary)}
.login{border-top:3px solid var(--color-primary)}
.login input{background:var(--color-input-bg)}
@media(max-width:1100px){.overview-context{align-items:flex-start;flex-direction:column}}
@media(max-width:600px){.login{margin:24px 16px;padding:24px}main{padding:16px}.panel,.stage,.overview-context{padding:20px}.bar-row{grid-template-columns:100px minmax(0,1fr) 45px;gap:8px}header{gap:12px}header button{padding:10px 12px}h1{overflow-wrap:anywhere}dl{grid-template-columns:1fr;gap:8px}dd{margin-bottom:12px}}

/* Gateway throughput: small multiples on one shared scale, recessive axis, text in text tokens. */
.traffic-chart{margin-top:24px}.chart-heading{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}.chart-heading h2{margin:0 0 6px}.chart-heading p{margin:0;font-size:12px}
.chart-legend{display:flex;gap:16px;font-size:11px;color:var(--color-text-muted);flex-shrink:0}.chart-legend span{display:flex;align-items:center;gap:6px}.swatch{width:10px;height:10px;border-radius:2px}
.swatch.ingress{background:var(--color-series-ingress)}.swatch.egress{background:var(--color-series-egress)}
.chart-row{display:grid;grid-template-columns:80px minmax(0,1fr) 72px;gap:14px;align-items:end;margin-top:20px}.chart-row svg{display:block;width:100%;height:auto}
.chart-label{font-size:12px;font-weight:700;color:var(--color-text);padding-bottom:4px}.chart-value{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--color-text);text-align:right;padding-bottom:4px}
.chart-base{stroke:var(--color-border);stroke-width:2}.bar.ingress{fill:var(--color-series-ingress)}.bar.egress{fill:var(--color-series-egress)}
.column .hit{fill:transparent}.column:hover .hit{fill:var(--color-primary-subtle)}.chart-scale{font-size:11px;margin:14px 0 0}
.rates-table{max-height:420px}
@media(max-width:600px){.chart-heading{flex-direction:column}.chart-row{grid-template-columns:1fr auto;gap:6px 12px}.chart-row svg{grid-column:1/-1;grid-row:2}}

/* Gateway data journey: TX -> gateway -> RX on one row, UteamUP IoT above the receiver. Packets move only on measured legs. */
.journey{margin-bottom:24px;overflow:hidden}
.journey-scene{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.3fr) minmax(0,1fr) minmax(0,1.3fr) minmax(0,1fr);grid-template-areas:"caption caption caption . iot" "caption caption caption . up" "tx ingress gw egress rx";margin:22px 0 8px}
.journey-caption{grid-area:caption;align-self:start;max-width:560px}.journey-caption .flow-map-note{margin-top:0}.journey-caption .flow-map-legend{border-top:0;padding-top:0;margin-top:12px}
.journey-node{display:flex;flex-direction:column;align-items:center;text-align:center;min-width:0}.journey-node .flow-device{margin:4px 0 12px}.journey-node strong{font-size:13px}
.node-tx{grid-area:tx}.node-gw{grid-area:gw}.node-rx{grid-area:rx}.node-iot{grid-area:iot}
.node-gw .flow-device{color:var(--color-primary-text);border-color:var(--color-primary-text)}.node-gw.health-online .flow-device{color:var(--color-success-text);border-color:var(--color-success-text)}
.node-gw.health-stale .flow-device{color:var(--color-warning-text);border-color:var(--color-warning-text)}.node-gw.health-fault .flow-device{color:var(--color-danger-text);border-color:var(--color-danger-text)}
.journey-leg{position:relative;min-width:0;color:var(--color-border)}.journey-leg.ingress{grid-area:ingress}.journey-leg.egress{grid-area:egress}.journey-leg.iot{grid-area:up;height:86px}
.journey-leg.ingress,.journey-leg.egress{align-self:start;height:96px;margin:0 -10px}
.journey-rail{position:absolute;left:0;right:0;top:69px;border-top:2px dashed currentColor}
.journey-leg.iot .journey-rail{left:50%;right:auto;top:6px;bottom:6px;border-top:0;border-left:2px dashed currentColor}
.journey-leg::after{content:"→";position:absolute;right:-4px;top:58px;font-size:20px;line-height:20px}.journey-leg.iot::after{content:"↑";right:auto;left:calc(50% - 5px);top:-6px}
.journey-leg.moving.ingress{color:var(--color-series-ingress)}.journey-leg.moving.egress{color:var(--color-series-egress)}
.journey-leg.moving .journey-rail{border-top-style:solid;opacity:.35}.journey-leg.waiting,.journey-leg.stale{color:var(--color-warning-text)}.journey-leg.error{color:var(--color-danger-text)}
.journey-label{position:absolute;left:0;right:0;top:80px;text-align:center;font-size:10px;line-height:1.5;color:var(--color-text-muted);overflow-wrap:anywhere}.journey-leg.moving .journey-label{color:var(--color-text)}
.journey-leg.iot .journey-label{left:auto;right:calc(50% + 14px);top:50%;width:170px;transform:translateY(-50%);text-align:right}
.journey-packet{--journey-speed:2.6s;position:absolute;left:0;top:63px;width:14px;height:14px;border-radius:4px;background:currentColor;box-shadow:0 0 0 2px var(--color-surface);opacity:0;animation:journey-across var(--journey-speed) linear infinite}
.steady .journey-packet{--journey-speed:1.7s}.fast .journey-packet{--journey-speed:.9s}
.journey-packet:nth-child(3){animation-delay:calc(var(--journey-speed) / -3)}.journey-packet:nth-child(4){animation-delay:calc(var(--journey-speed) * -2 / 3)}
.motion-paused .journey-packet{animation-play-state:paused}
@keyframes journey-across{0%{left:0;opacity:0}12%{opacity:1}88%{opacity:1}100%{left:calc(100% - 14px);opacity:0}}
@keyframes journey-down{0%{top:0;opacity:0}12%{opacity:1}88%{opacity:1}100%{top:calc(100% - 14px);opacity:0}}
@media(prefers-reduced-motion:reduce){.journey-packet{animation:none!important;opacity:1}.journey-packet:nth-child(n+3){display:none}.journey-leg .journey-packet{left:calc(50% - 7px)}}
@media(max-width:1100px) and (min-width:761px){.journey-leg.iot .journey-label{width:120px}}
@media(max-width:760px){.journey-scene{grid-template-columns:1fr;grid-template-areas:"tx" "ingress" "gw" "egress" "rx" "up" "iot" "caption";margin-top:20px}
.journey-caption{max-width:none;margin-top:20px;border-top:1px solid var(--color-border);padding-top:16px}
.journey-leg.ingress,.journey-leg.egress,.journey-leg.iot{height:84px;margin:6px 0}
.journey-rail,.journey-leg.iot .journey-rail{left:50%;right:auto;top:0;bottom:0;border-top:0;border-left:2px dashed currentColor}.journey-leg.moving .journey-rail{border-left-style:solid}
.journey-leg::after,.journey-leg.iot::after{content:"↓";left:calc(50% - 5px);right:auto;top:auto;bottom:-8px}
.journey-label,.journey-leg.iot .journey-label{left:calc(50% + 16px);right:0;top:50%;width:auto;transform:translateY(-50%);text-align:left}
.journey-packet{left:calc(50% - 6px);top:0;animation-name:journey-down}
@media(prefers-reduced-motion:reduce){.journey-leg .journey-packet{left:calc(50% - 6px);top:calc(50% - 7px)}}}
