/* Operations dashboard (static/ops.html, static/js/ops/opsDashboard.js). Dark like the map app and the review page;
   built for a second monitor or half a widescreen. Level colours match the Watchlist (ui/systemSummary.js). */
:root {
    --bg: #0f1115; --panel: #161922; --border: #2a303b; --text: #d7dbe0; --muted: #a3acb9;
    --mint: #43d2aa; --link: #8ec5ff;
    --green: #2ecc71; --yellow: #ffb400; --orange: #ff8c1a; --red: #e03131; --gray: #5b6472;
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--text); font: 13px/1.4 -apple-system, "Segoe UI", Helvetica, Arial, sans-serif; }
h1 { font-size: 18px; margin: 0; white-space: nowrap; }
h2 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin: 0 0 8px; }
button, input { font: inherit; color: var(--text); background: #1f2430; border: 1px solid var(--border); border-radius: 4px; padding: 3px 8px; }
button { cursor: pointer; }
button:hover { border-color: var(--mint); }
input { text-transform: uppercase; }
a { color: var(--link); }
.ops-muted { color: var(--muted); }

.ops-header { position: sticky; top: 0; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px;
    padding: 8px 14px; background: #12151b; border-bottom: 1px solid var(--border); }
.ops-scope { display: flex; align-items: center; gap: 8px; }
.ops-scope label { color: var(--muted); display: inline-flex; align-items: center; gap: 4px; }
.ops-seg { display: inline-flex; }
.ops-seg button { border-radius: 0; margin-left: -1px; }
.ops-seg button:first-child { border-radius: 4px 0 0 4px; margin-left: 0; }
.ops-seg button:last-child { border-radius: 0 4px 4px 0; }
.ops-seg button.on { background: #1d3a33; border-color: var(--mint); color: #fff; position: relative; }
.ops-status { color: var(--muted); margin-left: auto; }
.ops-status.bad { color: var(--orange); }
.ops-notify { font-size: 12px; }
.ops-link { white-space: nowrap; }
.ops-guest {
    padding: 1px 7px;
    border: 1px solid currentColor;
    border-radius: 9px;
    color: var(--mint);
    font-size: 11px;
    white-space: nowrap;
    cursor: help;
}

.ops-main { padding: 12px 14px 40px; display: flex; flex-direction: column; gap: 12px; }
.ops-panel { background: var(--panel); border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; min-width: 0; }

.ops-totals { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px; }
.ops-total { background: var(--panel); border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; display: flex; align-items: baseline; gap: 8px; }
.ops-total b { font-size: 26px; font-variant-numeric: tabular-nums; color: #fff; }
.ops-total span { color: var(--muted); }
.ops-total.warn { border-color: #6b4a1f; } .ops-total.warn b { color: #ffc36e; }
.ops-total.alert { border-color: #6b2323; } .ops-total.alert b { color: #ff8a8a; }

.ops-columns { display: grid; grid-template-columns: minmax(260px, 1fr) minmax(0, 2.4fr); gap: 12px; align-items: start; }
@media (max-width: 1100px) { .ops-columns { grid-template-columns: 1fr; } .ops-totals { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.ops-impacts { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.ops-impact { display: grid; grid-template-columns: 42px 1fr; column-gap: 8px; padding: 5px 6px; border-left: 3px solid var(--gray); background: #1a1e27; border-radius: 3px; }
.ops-impact-apt { font-weight: 700; color: #fff; }
.ops-impact-when { grid-column: 2; font-size: 11px; color: var(--muted); }
.ops-k-GS, .ops-k-rapt, .ops-k-weather_red, .ops-k-STOP { border-left-color: var(--red); }
.ops-k-GDP, .ops-k-over_rate, .ops-k-holding, .ops-k-weather_orange, .ops-k-AFP, .ops-k-FCA { border-left-color: var(--orange); }
.ops-k-diversions, .ops-k-cancels, .ops-k-APREQ { border-left-color: var(--yellow); }

.ops-hubs { display: flex; flex-direction: column; gap: 8px; }
.ops-empty { color: var(--muted); padding: 12px 0; }
.ops-hub { border: 1px solid var(--border); border-radius: 5px; padding: 8px 10px; background: #1a1e27; }
.ops-hub-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ops-dot { width: 11px; height: 11px; border-radius: 50%; flex: none; }
.ops-hub-code { font-size: 16px; color: #fff; }
.ops-hub-city { color: var(--muted); }
.ops-hub-stats { margin-left: auto; display: flex; gap: 10px; }
.ops-stat { color: var(--muted); white-space: nowrap; }
.ops-stat b { color: var(--muted); font-variant-numeric: tabular-nums; }
.ops-stat.on b { color: #ffc36e; }
.ops-hub-body { display: flex; gap: 12px; margin-top: 6px; align-items: flex-end; }
.ops-hub-left { flex: 1 1 auto; min-width: 0; }
.ops-factors { list-style: none; margin: 0 0 5px; padding: 0; }
.ops-factors li { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ops-src { font-size: 10px; padding: 0 4px; border-radius: 3px; background: #262c38; color: var(--muted); }
.lv-text-red { color: #ff8a8a; } .lv-text-orange { color: #ffb066; } .lv-text-yellow { color: #ffd166; }
.ops-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.ops-chip { font-size: 11.5px; padding: 1px 6px; border-radius: 3px; background: #262c38; color: var(--text); white-space: nowrap; }
.ops-chip.prog-GS { background: #5c1d1d; color: #ffd5d5; }
.ops-chip.prog-GDP, .ops-chip.prog-AFP { background: #5a3810; color: #ffe2bf; }
.ops-chip.prog-FCA, .ops-chip.prog-FEA { background: #3b3416; color: #fff0b3; }
.ops-chip.upcoming { opacity: 0.75; border: 1px dashed #8a8f99; }
.ops-chip.dim { color: var(--muted); }

.ops-strip-wrap { margin-top: 6px; }
.ops-strip { display: grid; grid-template-columns: 30px repeat(24, minmax(0, 1fr)); gap: 1px; }
.ops-cell { height: 10px; border-radius: 1px; }
.ops-cell.now { outline: 1px solid #fff; outline-offset: -1px; }
.ops-strip-labels { display: grid; grid-template-columns: 30px repeat(24, minmax(0, 1fr)); gap: 1px; font-size: 10px; color: var(--muted); }
.lv-red { background: var(--red); } .lv-orange { background: var(--orange); } .lv-yellow { background: var(--yellow); }
.lv-green { background: #1f7a4a; } .lv-gray { background: var(--gray); }

.ops-table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.ops-table th, .ops-table td { text-align: left; padding: 3px 8px; border-bottom: 1px solid var(--border); }
.ops-table th { color: var(--muted); font-weight: 600; }

/* New program at a hub: flashes until acknowledged (no sound, owner 2026-10-09). */
.ops-alert { position: fixed; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.55); }
.ops-alert[hidden] { display: none; }
.ops-alert-box { min-width: 340px; max-width: 560px; background: #2a1215; border: 3px solid var(--red); border-radius: 8px; padding: 16px 20px;
    animation: ops-flash 1s steps(2, jump-none) infinite; }
.ops-alert-box h2 { color: #fff; font-size: 16px; text-transform: none; letter-spacing: 0; }
.ops-alert-box ul { margin: 0 0 12px; padding-left: 18px; font-size: 14px; }
@keyframes ops-flash { 0% { border-color: var(--red); box-shadow: 0 0 0 0 rgba(224, 49, 49, 0.0); } 100% { border-color: #ffd5d5; box-shadow: 0 0 24px 4px rgba(224, 49, 49, 0.6); } }
@media (prefers-reduced-motion: reduce) { .ops-alert-box { animation: none; } }

/* Airport view (static/js/ops/opsAirportView.js). */
:root { --text-light: var(--text); --text-muted: var(--muted); --panel-bg: var(--panel); --panel-border: var(--border); }
.ops-overview[hidden], .ops-airport[hidden] { display: none; }
.ops-overview, .ops-airport { display: flex; flex-direction: column; gap: 12px; }
[data-open] { cursor: pointer; }
.ops-hub-head:hover .ops-hub-code, .ops-impact[data-open]:hover .ops-impact-apt { color: var(--mint); text-decoration: underline; }
.ops-apt-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.ops-apt-head .ops-hub-code { font-size: 22px; }
.ops-back { align-self: center; }
.ops-type { font-size: 11px; padding: 1px 6px; border-radius: 3px; color: #0b0f14; align-self: center; }
.ops-type-hub { background: #fbbf24; } .ops-type-outstation { background: #38bdf8; } .ops-type-alternate { background: #c084fc; }
.ops-apt-grid { display: grid; grid-template-columns: 1.3fr 1fr 1fr; gap: 12px; align-items: start; }
.ops-apt-grid.two { grid-template-columns: 1fr 1fr; }
/* Airport view (owner 2026-10-18): panels sit side by side down to a portrait monitor or half a screen, and stack
   only on a narrow window; departures beside arrivals. */
.ops-apt-top { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); align-items: stretch; }
.ops-apt-atc { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); align-items: stretch; }
.ops-apt-flights { display: grid; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); gap: 12px; align-items: start; }
.ops-flights-scope { margin: 0 0 -6px; font-size: 12px; }
.ops-raw { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px; margin-bottom: 6px; overflow-wrap: anywhere; }
.cat-VFR { color: #4ade80; } .cat-MVFR { color: #60a5fa; } .cat-IFR { color: #f87171; } .cat-LIFR { color: #e879f9; }
.ops-strip-n { grid-template-columns: 30px repeat(var(--n, 48), minmax(0, 1fr)); }
.ops-prog { padding: 3px 0; }
.ops-restr .rstr-row { margin: 2px 0; }
.ops-note { font-size: 11px; margin: 6px 0 0; }
.ops-two { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; }
.ops-two h3, .ops-panel h2 .ops-muted { font-size: 12px; font-weight: 400; text-transform: none; letter-spacing: 0; }
.ops-two h3 { color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 6px; }

/* Flights per hour vs rates. */
.ops-hstrip { display: flex; gap: 10px; margin: 4px 0 10px; }
.ops-hstrip-name { flex: none; width: 82px; font-size: 12px; }
.ops-hstrip-plot { flex: 1 1 auto; min-width: 0; }
.ops-hbars { position: relative; display: grid; grid-template-columns: repeat(24, minmax(0, 1fr)); gap: 2px; height: 56px; align-items: end; }
.ops-hcol { position: relative; height: 100%; background: rgba(255, 255, 255, 0.05); border-radius: 1px; }
.ops-hfill { position: absolute; left: 0; right: 0; bottom: 0; background: #60a5fa; border-radius: 1px; }
.ops-hcol.over .ops-hfill { background: #f59e0b; }
.ops-hcol.constrained::before { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 3px; background: #a78bfa; z-index: 1; }
.ops-hcol.incomplete { background-image: repeating-linear-gradient(135deg, rgba(160, 170, 180, 0.22) 0 2px, transparent 2px 4px); }
.ops-hrate { position: absolute; left: 0; right: 0; height: 2px; background: #fff; bottom: calc(56px * var(--rate) - 1px); pointer-events: none; }
.ops-hlabels { display: grid; grid-template-columns: repeat(24, minmax(0, 1fr)); gap: 2px; font-size: 10px; color: var(--muted); }

/* Departures / arrivals tables. */
.ops-flights { font-size: 12px; }
.ops-flights tr.past td { color: var(--muted); }
.ops-flights tr.st-cancelled td { color: #ff8a8a; text-decoration: line-through; }
.ops-flights tr.st-cancelled td:last-child { text-decoration: none; }
.ops-flights td.late { color: #ffc36e; font-weight: 600; }
.ops-flights td.retimed { color: var(--muted); font-style: italic; }
.ops-apt-flights .ops-panel { overflow-x: auto; }
.ops-flights th, .ops-flights td { padding: 3px 6px; white-space: nowrap; }
.ops-flights .num { text-align: right; }
.ops-flights th .ops-muted { font-weight: 400; }
.ops-flights-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 12px; margin-top: 8px; font-size: 12px; }
.ops-pager { display: inline-flex; align-items: center; gap: 8px; }
.ops-pager button:disabled { opacity: 0.4; cursor: default; border-color: var(--border); }
.ops-pager-n { font-variant-numeric: tabular-nums; color: var(--text); }
.ops-legend { font-size: 11px; }
.ops-impacts.solo .ops-impact { grid-template-columns: 1fr; }
.ops-impacts.solo .ops-impact-when { grid-column: 1; }

/* Now / Review tabs. */
.ops-tabs { display: inline-flex; }
.ops-tabs button { border-radius: 0; margin-left: -1px; }
.ops-tabs button:first-child { border-radius: 4px 0 0 4px; margin-left: 0; }
.ops-tabs button:last-child { border-radius: 0 4px 4px 0; }
.ops-tabs button[aria-selected="true"] { background: #1d3a33; border-color: var(--mint); color: #fff; position: relative; }
.ops-review-view[hidden], .ops-main[hidden] { display: none; }
.ops-review-view iframe { display: block; width: 100%; height: calc(100vh - 52px); border: 0; background: var(--bg); }

/* Outstation exceptions. */
.ops-out { display: flex; flex-direction: column; gap: 4px; }
.ops-out-row { display: grid; grid-template-columns: 54px 150px 1fr; gap: 8px; align-items: baseline; padding: 4px 6px; background: #1a1e27; border-radius: 3px; }
.ops-out-row .ops-hub-code { font-size: 14px; }
.ops-out-why { display: flex; flex-wrap: wrap; gap: 4px 10px; }
.ops-out-why span { white-space: nowrap; }
.ops-k-text-GS, .ops-k-text-rapt, .ops-k-text-weather_red, .ops-k-text-STOP { color: #ff8a8a; }
.ops-k-text-GDP, .ops-k-text-over_rate, .ops-k-text-holding, .ops-k-text-weather_orange, .ops-k-text-AFP, .ops-k-text-FCA { color: #ffb066; }
.ops-k-text-diversions, .ops-k-text-cancels, .ops-k-text-APREQ { color: #ffd166; }

/* Above the fold (2026-10-09 owner: "a square block for each hub so it can all be viewed in a single window"): the
   counters and the hub tiles fill the window under the header; opsDashboard.js gridFor/fitTiles sets --cols/--rows.
   Text inside a tile scales with the tile (container query units). Tile layout (owner 2026-10-18): labelled
   sections top to bottom, arrivals vs AAR beside departures vs ADR, the hourly impact strip last; only the weather
   factors give way when a tile is short, and the hour labels drop out before anything overlaps. */
.ops-fold { height: calc(100vh - var(--hdr, 50px) - 24px); min-height: 360px; display: flex; flex-direction: column; gap: 8px; }
.ops-fold .ops-totals { flex: none; }
.ops-fold .ops-total { padding: 4px 10px; }
.ops-fold .ops-total b { font-size: 22px; }
.ops-tiles { flex: 1 1 auto; min-height: 0; display: grid; gap: 8px;
    grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)); grid-template-rows: repeat(var(--rows, 2), minmax(0, 1fr)); }
.ops-tiles > .ops-empty, .ops-tiles > .ops-muted { grid-column: 1 / -1; }
/* Container units resolve against the tile only in its children (on the tile itself they'd fall back to the
   window), so the text size is set on each row. */
.ops-tile { container-type: size; min-width: 0; min-height: 0; overflow: hidden; cursor: pointer;
    display: flex; flex-direction: column; gap: 5px; padding: 8px 12px;
    background: var(--panel); border: 1px solid var(--border); border-left: 5px solid #2f3846; border-radius: 6px; }
.ops-tile > * { flex: none; min-width: 0; font-size: clamp(11px, 5.6cqmin, 17px); }
.ops-tile:hover { border-color: var(--mint); }
.ops-tile.sev-3 { border-left-color: var(--red); background: linear-gradient(90deg, rgba(224, 49, 49, 0.10), transparent 40%), var(--panel); }
.ops-tile.sev-2 { border-left-color: var(--orange); }
.ops-tile.sev-1 { border-left-color: var(--yellow); }

.ops-thead { display: flex; align-items: center; gap: 0.5em; min-width: 0; }
.ops-thead .ops-dot { width: 0.8em; height: 0.8em; }
.ops-tcode { font-size: clamp(17px, 10cqmin, 40px); line-height: 1; color: #fff; }
.ops-tcity { flex: 1 1 auto; min-width: 0; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ops-tlevel { flex: none; font-size: 0.85em; font-weight: 600; white-space: nowrap; }
.lv-text-green { color: #4ade80; } .lv-text-gray { color: var(--muted); }

.ops-tcounts { display: flex; gap: 0.9em; white-space: nowrap; overflow: hidden; }
.ops-tcounts b { color: #ffc36e; font-size: 1.15em; font-variant-numeric: tabular-nums; }

/* A labelled row: a fixed label column, then the content. */
.ops-tsec { display: grid; grid-template-columns: 5.4em minmax(0, 1fr); column-gap: 0.5em; align-items: baseline; }
.ops-tlabel { font-size: max(9px, 0.72em); text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); white-space: nowrap; }
.ops-ttop { font-weight: 600; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ops-tile .ops-chips { font-size: 0.85em; flex-wrap: nowrap; overflow: hidden; }
.ops-tile .ops-chip { font-size: 1em; }
.ops-tile > .ops-tfactors { flex: 1 1 auto; min-height: 1.3em; overflow: hidden; align-content: start; }
.ops-tfactors .ops-factors { margin: 0; font-size: 0.92em; }
.ops-tfactors .ops-factors li { line-height: 1.35; }
.ops-tfactors .ops-more { color: var(--muted); }

/* Arrivals vs AAR | departures vs ADR, next 6 hours. */
.ops-tdemand { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 1.2em; padding-top: 0.3em; border-top: 1px solid var(--border); }
.ops-demand { margin: 0; min-width: 0; }
.ops-demand figcaption { display: flex; align-items: baseline; gap: 0.4em; font-size: 0.85em; margin-bottom: 0.2em; white-space: nowrap; overflow: hidden; }
.ops-demand figcaption b { color: var(--text); }
.ops-over { color: #ffc36e; font-size: 0.9em; }
.ops-dplot { position: relative; display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 2px; height: clamp(16px, 12cqh, 70px); align-items: end; }
.ops-dcol { position: relative; height: 100%; background: rgba(255, 255, 255, 0.06); border-radius: 1px; }
.ops-dfill { position: absolute; left: 0; right: 0; bottom: 0; background: #60a5fa; border-radius: 1px; }
.ops-demand-dep .ops-dfill { background: #2dd4bf; }
.ops-dcol.over .ops-dfill { background: #f59e0b; }
.ops-drate { position: absolute; left: -2px; right: -2px; height: 2px; background: #fff; bottom: calc(var(--rate) * 100% - 1px); pointer-events: none; }
.ops-dhours { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 2px; font-size: 0.68em; color: var(--muted); text-align: center; font-variant-numeric: tabular-nums; }

/* Hourly impact: now + 12 hours, every third hour labelled. */
.ops-tstrip-sec { align-items: start; }
.ops-tstrip { display: grid; grid-template-columns: repeat(var(--n, 13), minmax(0, 1fr)); gap: 1px; }
.ops-tstrip .ops-cell { height: clamp(8px, 5cqh, 18px); }
.ops-tstrip-hours { font-size: 0.68em; color: var(--muted); font-variant-numeric: tabular-nums; }
.ops-tstrip-hours span { overflow: visible; white-space: nowrap; }

/* Short tiles: hour labels go before anything can overlap; very short ones drop the counts' words too. */
@container (max-height: 240px) { .ops-dhours, .ops-tstrip-hours { display: none; } .ops-tcode { font-size: 17px; } }
@container (max-width: 360px) { .ops-over { display: none; } }
/* Narrow tiles: a slimmer label column; the graphs keep just "AAR 84" / "ADR 70" and every other hour. */
@container (max-width: 290px) {
    .ops-tsec { grid-template-columns: 4.3em minmax(0, 1fr); column-gap: 0.4em; }
    .ops-tlabel { font-size: max(9px, 0.64em); letter-spacing: 0.02em; }
    .ops-dname, .ops-tcity { display: none; }
    .ops-tdemand { column-gap: 0.7em; }
    .ops-dhours span:nth-child(even) { visibility: hidden; }
    .ops-tstrip-hours span:not(:first-child) { visibility: hidden; }
}

.ops-below { display: flex; flex-direction: column; gap: 12px; }
.ops-below .ops-columns { grid-template-columns: 1fr 1fr; }
@media (max-width: 1100px) { .ops-below .ops-columns { grid-template-columns: 1fr; } }
