/* ======================================================================
   LoRa flood simulator (sim-panel / simulator.js)
   ----------------------------------------------------------------------
   Split out of style.css. Loaded AFTER it and BEFORE style-mobile.css;
   see the load order in index.html.
   ====================================================================== */

/* --- LoRa flood simulator (sim-panel / simulator.js) ------------------ */

/* These lists can grow large (loading real repeaters alone can mean
   dozens of rows) — each still gets its own capped scroll region so one
   long list can't push every section after it out of view within its
   column, but the cap itself is generous now that setup/results each have
   a full workspace column (not a 320px sidebar) to work with. */
#sim-node-list, #sim-message-list, #sim-results-log, #sim-per-node-list, #sim-suggestions-list, #sim-bottleneck-list, #sim-unmodeled-list {
  max-height: 320px;
  overflow-y: auto;
}

.sim-node-badge {
  font-size: 0.62rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 0.1rem 0.35rem;
  border-radius: 4px;
  flex-shrink: 0;
}
.sim-node-badge.sim-badge-planned { background: rgba(74,222,128,0.18); color: #4ade80; }
.sim-node-badge.sim-badge-real { background: rgba(56,189,248,0.18); color: #38bdf8; }
.sim-node-badge.sim-badge-companion { background: rgba(251,146,60,0.18); color: #fb923c; }
.sim-node-badge.sim-badge-region { background: rgba(168,85,247,0.18); color: #c084fc; }
/* Marks a row as a real CoreScope measurement rather than a model output —
   cyan, matching the observed-traffic line colour on the map. */
.sim-node-badge.sim-badge-observed { background: rgba(34,211,238,0.18); color: #22d3ee; }
.sim-node-badge.sim-badge-direct { background: rgba(250,204,21,0.18); color: #facc15; }
/* Item 17 — a sender's own configured path-hash size (1-3 bytes). */
.sim-node-badge.sim-badge-hashsize { background: rgba(148,163,184,0.18); color: #94a3b8; }
/* Phase 8 — a fixed background transmission of reconstructed real traffic. */
.sim-node-badge.sim-badge-background { background: rgba(100,116,139,0.22); color: #94a3b8; }

#sim-message-cancel-edit.hidden { display: none; }
#sim-setup-import-file.hidden { display: none; }
#sim-packet-modal-back.hidden { display: none; }
#sim-packet-modal-back {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 0.72rem;
  cursor: pointer;
  padding: 0 0.5rem 0 0;
  margin-right: 0.3rem;
}
#sim-packet-modal-back:hover { color: var(--text); }

#sim-packet-modal-sent-section.hidden { display: none; }
#sim-packet-modal-checklist-section.hidden { display: none; }
#sim-bottleneck-replay-section.hidden { display: none; }

.sim-checklist-row { cursor: pointer; }
.sim-checklist-row:hover { background: rgba(255,255,255,0.08); }
.sim-checklist-row.sim-checklist-yes { border-left: 3px solid #4ade80; }
.sim-checklist-row.sim-checklist-no { border-left: 3px solid #f87171; }
.sim-checklist-row.sim-checklist-origin { border-left: 3px solid #a855f7; }
.sim-checklist-status {
  margin-left: auto;
  font-size: 0.68rem;
  font-weight: 700;
  white-space: nowrap;
  cursor: help;
}

.sim-message-row { cursor: pointer; }
.sim-message-row:hover { background: rgba(255,255,255,0.08); }
.sim-message-row-selected {
  background: rgba(74,222,128,0.12) !important;
  border-left: 3px solid #4ade80;
}

/* Phase 4 work item 6 — policy profile breakdown. Deliberately no colour
   coding for profile identity (word labels only, see
   renderPolicyProfileSummary's own doc comment): the community guides
   this feature is built from DO assign a colour per profile, but a colour
   needs a legend to decode and doesn't survive being read aloud or pasted
   into a message. */
.sim-policy-profile-row { cursor: pointer; }
.sim-policy-profile-row:hover { background: rgba(255,255,255,0.08); }
.sim-policy-profile-chevron { margin-left: auto; opacity: 0.6; }
.sim-policy-profile-detail-approx {
  font-style: italic;
  color: var(--text-muted);
}
/* No global `.hidden { display: none; }` rule exists in this file — every
   hideable element needs its own `#id.hidden` rule (a bug this project
   has already hit once for exactly this reason). */
#sim-policy-profile-detail.hidden { display: none; }
#sim-policy-source-note.hidden { display: none; }

.sim-message-details-btn {
  margin-left: 0.5rem;
  font-size: 0.62rem;
  padding: 0.05rem 0.35rem;
}

.sim-packet-path {
  font-weight: 400;
  font-size: 0.65rem;
  color: var(--text-muted);
}

#sim-add-companion.active {
  background: #fb923c;
  border-color: #fb923c;
  color: #1c1206;
}

.sim-list-item.sim-collided { border-left: 3px solid #f87171; }
.sim-list-item.sim-clean { border-left: 3px solid #4ade80; }
.sim-list-item.sim-dropped { border-left: 3px solid #facc15; }

/* Packet inspector rows (see renderPacketReceptionRows) — a wider modal
   than the shared 560px default (see #sim-nodes-modal above for the same
   pattern) so the path, time/hop, and reason columns have room without
   wrapping into each other. */
#sim-packet-modal { width: min(920px, 100%); }

.sim-packet-row { flex-direction: column; align-items: stretch; gap: 0.3rem; }
.sim-packet-row-top { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; }
.sim-packet-row-bottom {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  font-size: 0.68rem;
  color: var(--text-muted);
}
/* .sim-packet-context deliberately wraps (unlike time/hop, always short) —
   item 12's relay-link suffix ("⤴ relaying what arrived at 1,240ms") can
   push it past a single line at narrower widths. */
.sim-packet-context { white-space: normal; }
.sim-packet-time, .sim-packet-hop { white-space: nowrap; }

.sim-packet-reason {
  margin-left: auto;
  font-size: 0.65rem;
  font-weight: 700;
  padding: 0.1rem 0.45rem;
  border-radius: 4px;
  white-space: nowrap;
  cursor: help;
}
.sim-packet-reason.sim-reason-relayed { background: rgba(74,222,128,0.18); color: #4ade80; }
.sim-packet-reason.sim-reason-received { background: rgba(56,189,248,0.18); color: #38bdf8; }
.sim-packet-reason.sim-reason-dropped { background: rgba(250,204,21,0.18); color: #facc15; }
.sim-packet-reason.sim-reason-collided { background: rgba(248,113,113,0.18); color: #f87171; }
.sim-packet-reason.sim-reason-captured { background: rgba(167,139,250,0.18); color: #a78bfa; }
/* tx_busy (item 13) — a miss, not a collision (nothing was ever heard) and
   not an active drop decision — deliberately distinct from both the red
   sim-reason-collided and the yellow sim-reason-dropped. */
.sim-packet-reason.sim-reason-missed { background: rgba(148,163,184,0.20); color: #94a3b8; }

.sim-txrx-badge {
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  padding: 0.1rem 0.3rem;
  border-radius: 4px;
  flex-shrink: 0;
}
.sim-txrx-badge.sim-txrx-tx { background: rgba(168,85,247,0.22); color: #c084fc; }
.sim-txrx-badge.sim-txrx-rx { background: rgba(56,189,248,0.18); color: #38bdf8; }
/* Distinct from plain TX (the origin's own first send) — a repeater's own
   re-transmission of someone else's packet (item 12). */
.sim-txrx-badge.sim-txrx-relay { background: rgba(74,222,128,0.20); color: #4ade80; }

/* item 12 — linking an RX row to the relay TX it caused (or vice versa).
   sim-jump-link is the small clickable "⤴/⤵ ..." text; sim-row-linked is
   the hover state applied to BOTH halves of a pair via their shared
   data-link-key, so hovering either highlights the other. */
.sim-packet-relay-link { color: var(--text-muted); font-size: 0.65rem; }
.sim-jump-link { cursor: pointer; text-decoration: underline dotted; }
.sim-jump-link:hover { color: #4ade80; }
.sim-linkable-row { cursor: default; }
.sim-list-item.sim-row-linked { background: rgba(74,222,128,0.10); }
/* Reuses the same flash-on-jump treatment #sim-nodes-modal's own row
   highlight uses (see openNodesModal), scoped here to the plain-div
   activity rows rather than table rows. */
.sim-list-item.sim-row-highlight {
  background: rgba(74,222,128,0.18);
  transition: background 1.5s ease;
}

.sim-suggestion-rank {
  font-weight: 700;
  color: var(--accent);
  flex-shrink: 0;
  width: 1.4rem;
}
.sim-suggestion-rate { color: var(--text-muted); font-size: 0.68rem; }
.sim-suggestion-rate.sim-rate-better { color: #4ade80; }

.sim-marker-icon {
  background: #a855f7;
  border: 2px solid #fff;
  border-radius: 50%;
  width: 12px;
  height: 12px;
  box-shadow: 0 0 0 2px rgba(168,85,247,0.4);
}

/* Companion locations are draggable, user-placed points, distinct from
   fixed repeater sites — a diamond (rotated square) instead of a dot
   reads as "a portable device" at a glance, plus its own colour. */
.sim-marker-companion {
  background: #fb923c;
  border: 2px solid #fff;
  width: 11px;
  height: 11px;
  transform: rotate(45deg);
  box-shadow: 0 0 0 2px rgba(251,146,60,0.45);
  cursor: grab;
}

/* --- repeater rankings table (docked + full-window "pop out") --------- */

.sim-rankings-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.75rem;
}
.sim-rankings-table th, .sim-rankings-table td {
  text-align: right;
  padding: 0.3rem 0.5rem;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.sim-rankings-table th:first-child, .sim-rankings-table td:first-child {
  text-align: left;
}
.sim-rankings-table th {
  color: var(--text-muted);
  font-weight: 600;
  cursor: pointer;
  user-select: none;
  position: sticky;
  top: 0;
  background: var(--bg);
}
.sim-rankings-table th.sim-rank-sorted { color: var(--accent); }
.sim-rankings-table tbody tr:hover { background: rgba(255,255,255,0.05); }
.sim-rankings-table td.sim-rank-good { color: #4ade80; }
.sim-rankings-table td.sim-rank-bad { color: #f87171; }

#sim-rankings-section .plan-section-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
#sim-rankings-expand {
  font-size: 0.68rem;
  padding: 0.15rem 0.4rem;
}
#sim-rankings-table-container {
  max-height: 260px;
  overflow-y: auto;
}

#sim-rankings-fullwindow {
  position: fixed;
  inset: 0;
  z-index: 5000;
  background: var(--bg);
  display: flex;
  flex-direction: column;
  padding: 1rem 1.5rem;
}
#sim-rankings-fullwindow.hidden { display: none; }
.sim-rankings-fullwindow-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 700;
  font-size: 1rem;
  margin-bottom: 0.75rem;
}
#sim-rankings-fullwindow-body {
  flex: 1;
  overflow-y: auto;
}
#sim-rankings-fullwindow-body .sim-rankings-table { font-size: 0.85rem; }
#sim-rankings-fullwindow-body .sim-rankings-table th, #sim-rankings-fullwindow-body .sim-rankings-table td {
  padding: 0.5rem 0.75rem;
}

/* --- sim modal system ---------------------------------------------------
   Every heavier chunk of simulator output (repeater config, message
   senders, results, bottleneck analysis, predicted settings) lives in one
   of these instead of a permanently-docked panel section — keeps the side
   panel itself a short, fixed list of controls. Centered dialogs, not a
   full-viewport takeover (that's reserved for the rankings "Expand" view,
   which explicitly wants the whole window — see #sim-rankings-fullwindow
   above). One backdrop, several possible .sim-modal children; openModal
   hides every other one first, so only one is ever visible at a time. */

#sim-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 4000;
  background: rgba(0,0,0,0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
}
#sim-modal-backdrop.hidden { display: none; }

.sim-modal {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.5);
  width: min(560px, 100%);
  max-height: 85vh;
  display: flex;
  flex-direction: column;
}
.sim-modal.hidden { display: none; }

/* Repeaters & settings has a wide table (label + 11 setting columns +
   actions) that doesn't fit the shared 560px width — give it more room
   specifically, rather than widening every modal. The table itself still
   scrolls horizontally within sim-config-table-scroll below for whatever
   doesn't fit even at this width. */
#sim-nodes-modal { width: min(1040px, 100%); }
/* Same reasoning as #sim-nodes-modal above — the adaptive optimizer's own
   modal carries two full-width tables (every repeater, and every round),
   which is exactly why it's a separate modal rather than another section
   stacked inside #sim-predictions-modal. */
#sim-optimize-modal { width: min(1040px, 100%); }

.sim-modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: 700;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--border);
}
.sim-modal-header button {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 1rem;
  cursor: pointer;
}
.sim-modal-body {
  padding: 0.75rem 1rem;
  overflow-y: auto;
}
.sim-modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 0.4rem;
  padding: 0.6rem 1rem;
  border-top: 1px solid var(--border);
}

/* --- "Repeaters & settings" table (inside #sim-nodes-modal) ------------ */

.sim-bulk-apply-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  margin-bottom: 0.6rem;
  padding: 0.4rem 0.5rem;
  background: rgba(255,255,255,0.04);
  border-radius: 6px;
}
.sim-bulk-apply-label { font-size: 0.7rem; color: var(--text-muted); flex-shrink: 0; }
.sim-bulk-apply-row input[type="number"] { width: 4.5rem; }
.sim-bulk-apply-row input, .sim-bulk-apply-row select {
  font-size: 0.72rem;
  padding: 0.2rem 0.3rem;
}
.sim-bulk-apply-row button { font-size: 0.72rem; padding: 0.25rem 0.5rem; }

.sim-config-table-scroll { max-height: 50vh; overflow-y: auto; overflow-x: auto; }

.sim-config-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.75rem;
}
.sim-config-table th, .sim-config-table td {
  text-align: left;
  padding: 0.3rem 0.4rem;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.sim-config-table th { color: var(--text-muted); font-weight: 600; }
/* Sticky header + sticky first column (item 10G) — a long/wide node table
   otherwise scrolls the node's own label out of view in both directions at
   once, the one column you actually need to keep visible while scanning. */
.sim-config-table thead th { position: sticky; top: 0; background: var(--bg); z-index: 1; }
.sim-config-table th.sim-col-sticky, .sim-config-table td.sim-col-sticky {
  position: sticky;
  left: 0;
  background: var(--bg);
  z-index: 2;
  box-shadow: 1px 0 0 var(--border);
}
.sim-config-table thead th.sim-col-sticky { z-index: 3; }
/* Item 16 — Duty cycle/Received result columns, shown only once a run
   exists (see renderNodesModalTable's own hidden-class toggle). */
.sim-config-table .sim-results-col.hidden { display: none; }
.sim-config-table td.sim-results-col { color: var(--text-muted); white-space: nowrap; }
.sim-config-table input[type="number"], .sim-config-table input[type="text"] {
  width: 3.6rem;
  font-size: 0.72rem;
  padding: 0.15rem 0.25rem;
}
.sim-config-table input[type="text"][data-field="regions"] { width: 6.5rem; }
.sim-config-table select {
  font-size: 0.72rem;
  padding: 0.15rem 0.25rem;
  background: var(--card-bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 4px;
}
.sim-config-table td.sim-checkbox-cell { text-align: center; }
.sim-radio-cell { min-width: 11rem; }
.sim-radio-preset-select { display: block; width: 100%; margin-bottom: 0.2rem; }
.sim-radio-fields { display: flex; gap: 0.2rem; }
.sim-radio-fields input { width: 2.5rem !important; }
.sim-config-table tr.sim-row-highlight {
  background: rgba(74,222,128,0.18);
  transition: background 1.5s ease;
}
/* Item 15b — the knee row in the stress-test capacity curve table, a
   persistent marker rather than sim-row-highlight's fade-out flash. */
.sim-config-table tr.sim-knee-row { background: rgba(74,222,128,0.14); font-weight: 700; }

