/* Winterfield Health - FHIR API diagnostics console.

   Loaded only by fhir-diagnostics.php, on top of portal.css, and holding only
   what that one page needs. The console is a workstation screen and belongs to
   the portal's visual family rather than the marketing site's, but it is not
   behind the portal login, so it is a separate file: nothing else should carry
   its weight and nothing here should leak into the portal.

   Terracotta stays the brand color and is spent on links and the live pulse.
   Call outcomes use the clinical status colors already defined in portal.css,
   because a 500 and an abnormal lab want the same red, and inventing a second
   red for the same idea is how a palette comes apart. */

.diag-page {
  display: grid;
  gap: 20px;
  padding-bottom: 60px;
  align-content: start;
}

/* ---------- panel head, live control ---------- */

.diag-head { align-items: flex-start; }
.diag-sub {
  font-size: 12.5px;
  color: var(--wf-muted);
  margin: 5px 0 0;
  max-width: 62ch;
}
.diag-controls {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: none;
}

/* The pulse is the whole point of the word "live": a static label claiming to
   be live is indistinguishable from a page that has quietly stopped polling. */
.diag-live {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font: 700 11px var(--wf-sans);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--wf-muted);
  white-space: nowrap;
}
.diag-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--wf-terracotta);
  animation: diag-pulse 2s infinite ease-in-out;
}
.diag-live.is-paused .diag-dot {
  background: var(--wf-muted);
  animation: none;
}
@keyframes diag-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .35; transform: scale(.82); }
}
@media (prefers-reduced-motion: reduce) {
  .diag-dot { animation: none; }
}

/* ---------- the filter bar ---------- */

/* Plain form controls submitting a GET, so filtering works without JavaScript;
   the poll re-reads the same query string. Labels sit inline with their
   controls because the bar is one row of small decisions, not a form. */
.diag-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  margin: 0 0 12px;
  padding: 10px 12px;
  background: var(--wf-tint);
  border-radius: 6px;
  font-size: 12.5px;
  color: var(--wf-body);
}
.diag-filter {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.diag-filter select,
.diag-filter input[type="datetime-local"] {
  font: inherit;
  font-size: 12.5px;
  color: inherit;
  background: var(--wf-panel);
  border: 1px solid var(--wf-hairline-strong);
  border-radius: 4px;
  padding: 3px 6px;
}
.diag-filter-check { cursor: pointer; }
.diag-filter-check input { margin: 0; }
.diag-filter-clear { font-size: 12.5px; }

/* The quick ranges and the collapsed granular block each take a full row so
   the main row stays scannable. <details> keeps the rare filters out of the
   way without JavaScript. */
.diag-presets {
  flex-basis: 100%;
  color: var(--wf-muted);
}
.diag-presets a { margin-right: 10px; }
.diag-more { flex-basis: 100%; }
.diag-more summary {
  cursor: pointer;
  color: var(--wf-muted);
  font-size: 12.5px;
}
.diag-more-fields {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  padding: 8px 0 2px;
}
.diag-more-fields input[type="text"],
.diag-more-fields input[type="number"] {
  font: inherit;
  font-size: 12.5px;
  color: inherit;
  background: var(--wf-panel);
  border: 1px solid var(--wf-hairline-strong);
  border-radius: 4px;
  padding: 3px 6px;
}

.diag-export { margin-left: 8px; }
.diag-more-rows {
  font-size: 12.5px;
  margin: 10px 0 0;
}

.diag-counts {
  font-size: 12.5px;
  color: var(--wf-body);
  margin: 0 0 14px;
  font-variant-numeric: tabular-nums;
}
.diag-failed { font-weight: 700; }
.diag-counts .is-bad { color: var(--wf-abnormal); }

/* ---------- the table ---------- */

.diag-table { font-size: 13px; }
.diag-table td { vertical-align: top; }

/* A four pixel edge carries the outcome down the whole row, so a failure is
   findable while scrolling without reading the status column. Color is never
   the only signal: the Result cell always spells the code out in words. */
.diag-table tbody tr { border-left: 4px solid transparent; }
.diag-table tbody tr td:first-child { padding-left: 10px; }
.diag-row.diag-ok       { border-left-color: rgba(74, 93, 67, .5); }
.diag-row.diag-redirect { border-left-color: rgba(138, 90, 18, .5); }
.diag-row.diag-client   { border-left-color: var(--wf-caution); }
.diag-row.diag-server   { border-left-color: var(--wf-abnormal); }
.diag-row.diag-none     { border-left-color: var(--wf-hairline-strong); }

/* Rows are only clickable once the script is running. Without it the Details
   link in the last cell is the way in, and a pointer cursor over a row that
   does nothing would be a lie. */
.js-diag .diag-row { cursor: pointer; }
.js-diag .diag-row.is-open { background: rgba(233, 161, 59, .18); }

.diag-method {
  font: 700 10.5px var(--wf-mono);
  letter-spacing: .06em;
  color: var(--wf-body);
  background: var(--wf-tint);
  border-radius: 3px;
  padding: 2px 6px;
}

.diag-target { min-width: 260px; }
.diag-target code {
  font: 400 12px var(--wf-mono);
  color: var(--wf-ink);
  word-break: break-all;
}
.diag-second {
  display: block;
  font-size: 11.5px;
  color: var(--wf-muted);
  margin-top: 3px;
}
.diag-msg { color: var(--wf-abnormal); }
.diag-row.diag-ok .diag-msg { color: var(--wf-muted); }

.diag-client { font-size: 12.5px; }
.diag-client .t-code { display: block; margin-top: 2px; }

.diag-status {
  display: inline-block;
  font: 700 10.5px var(--wf-sans);
  letter-spacing: .04em;
  border-radius: 3px;
  padding: 3px 7px;
  white-space: nowrap;
}
.diag-status-ok       { background: #E8EDE4; color: var(--wf-normal); }
.diag-status-redirect { background: var(--wf-caution-bg); color: var(--wf-caution); }
.diag-status-client   { background: var(--wf-caution-bg); color: var(--wf-caution); }
.diag-status-server   { background: var(--wf-abnormal-bg); color: var(--wf-abnormal); }
.diag-status-none     { background: var(--wf-tint); color: var(--wf-muted); }

.diag-open { font-size: 12.5px; }

/* ---------- empty state and footnote ---------- */

.diag-empty {
  border: 1px dashed var(--wf-hairline-strong);
  border-radius: 5px;
  padding: 18px 20px;
  margin-top: 14px;
  font-size: 13.5px;
  color: var(--wf-body);
}
.diag-empty p { margin: 0 0 10px; }
.diag-empty p:last-child { margin-bottom: 0; }

.diag-foot {
  font-size: 12px;
  margin: 14px 0 0;
  max-width: 78ch;
}

.diag-pre {
  font: 400 11.5px/1.5 var(--wf-mono);
  background: var(--wf-tint);
  border: 1px solid var(--wf-hairline);
  border-radius: 4px;
  padding: 10px 12px;
  margin: 0 0 14px;
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
  color: var(--wf-ink);
}
.diag-resp { max-height: 340px; overflow-y: auto; }

/* ---------- detail, in the panel and in the dialog ---------- */

.diag-detail-line {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
  margin: 0 0 12px;
}
.diag-detail-line code {
  font: 400 12.5px var(--wf-mono);
  word-break: break-all;
}
.diag-detail-msg {
  font-size: 13px;
  border-radius: 4px;
  padding: 9px 12px;
  margin: 0 0 14px;
}
.diag-detail-msg-ok, .diag-detail-msg-redirect {
  background: var(--wf-tint);
  color: var(--wf-body);
}
.diag-detail-msg-client { background: var(--wf-caution-bg); color: var(--wf-caution); }
.diag-detail-msg-server, .diag-detail-msg-none {
  background: var(--wf-abnormal-bg);
  color: var(--wf-abnormal);
}

.diag-detail-h {
  font: 700 10.5px var(--wf-sans);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--wf-muted);
  margin: 0 0 7px;
}

/* The cell rules are drawn by a ring on each cell rather than by a colored gap
   showing through the container. A grid of auto-fit columns almost never fills
   its last row exactly, and with the gap technique that leftover space renders
   as a slab of gray with nothing in it. */
.diag-facts, .diag-headers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(235px, 1fr));
  gap: 1px;
  background: var(--wf-panel);
  border: 1px solid var(--wf-hairline);
  border-radius: 4px;
  overflow: hidden;
  margin: 0 0 18px;
}
.diag-facts > div, .diag-headers > div {
  background: var(--wf-panel);
  box-shadow: 0 0 0 1px var(--wf-hairline);
  padding: 9px 12px;
}
.diag-facts dt, .diag-headers dt {
  font: 700 10px var(--wf-sans);
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--wf-muted);
  margin: 0 0 3px;
}
.diag-facts dd, .diag-headers dd {
  margin: 0;
  font: 400 12.5px var(--wf-mono);
  color: var(--wf-ink);
  word-break: break-all;
}
/* Headers are one long value each, so they read better full width. */
.diag-headers { grid-template-columns: 1fr; }

.diag-note {
  font-size: 12px;
  color: var(--wf-muted);
  margin: 0;
  max-width: 72ch;
}

/* ---------- click to copy ----------
   Every block holding a real value is a copy target.

   **The icon belongs to the block under the pointer and nothing else.** It first
   shipped visible at low contrast on all of them at once, on the reasoning that
   a touch screen has no hover and an affordance nobody can discover is not one.
   Dave read the result on 2026-07-30 and the reasoning did not survive contact:
   twenty-two dim icons down one panel is visual noise, and it stops pointing at
   anything in particular.

   The touch argument was right, so it is kept where it applies rather than
   thrown away. `@media (hover: hover)` scopes the reveal to devices that can
   hover; a touch screen still gets every icon, permanently. The copied and
   blocked states override the reveal below, so the confirmation does not vanish
   with the pointer. */

.copyable { position: relative; }
.copyable > dd, .copyable > code { padding-right: 22px; }

.copy-btn {
  position: absolute;
  top: 5px;
  right: 5px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 1px solid transparent;
  border-radius: 4px;
  background: transparent;
  color: var(--wf-muted);
  opacity: .55;
  padding: 3px 5px;
  line-height: 0;
  cursor: pointer;
  transition: opacity .12s, color .12s, background .12s, border-color .12s;
}

@media (hover: hover) {
  .copy-btn { opacity: 0; }
  /* focus-within, not just :focus, so tabbing to the button reveals it while
     the pointer is somewhere else entirely. */
  .copyable:hover .copy-btn,
  .copyable:focus-within .copy-btn { opacity: 1; }
}
.copy-btn:hover {
  opacity: 1;
  color: var(--wf-terracotta);
  background: var(--wf-panel);
  border-color: var(--wf-hairline-strong);
}
.copy-btn:focus-visible {
  opacity: 1;
  outline: 2px solid var(--wf-terracotta);
  outline-offset: 1px;
}

/* The whole block is the click target once the script is running. Text
   selection still wins: the handler ignores a click that ends a selection, so
   dragging across a response to grab one field keeps working. */
.js-diag .copyable { cursor: pointer; }
.js-diag .copyable:hover { background: rgba(233, 161, 59, .07); }
.js-diag .copy-block:hover { background: transparent; }

.copy-ok, .copy-said, .copy-said-failed { display: none; }
.copy-said, .copy-said-failed {
  font: 700 10px var(--wf-sans);
  letter-spacing: .06em;
  text-transform: uppercase;
  line-height: 1;
}
.copyable.is-copied .copy-btn {
  opacity: 1;
  color: var(--wf-normal);
  background: #E8EDE4;
  border-color: rgba(74, 93, 67, .35);
}
.copyable.is-copied .copy-i { display: none; }
.copyable.is-copied .copy-ok,
.copyable.is-copied .copy-said { display: inline-block; }

/* A refused clipboard write says so. It said "Copied" in red until 2026-07-30,
   which is the worst of both: the reader walks away believing they have the
   value on their clipboard when they have whatever was there before. */
.copyable.is-failed .copy-btn {
  opacity: 1;
  color: var(--wf-abnormal);
  background: var(--wf-abnormal-bg);
  border-color: rgba(168, 52, 31, .35);
}
.copyable.is-failed .copy-said-failed { display: inline-block; }

/* A pre already has its own padding and border, so the button sits inside it
   rather than over the first line of text. */
.copy-block .copy-btn { top: 6px; right: 8px; }
.copy-block .diag-pre { padding-right: 46px; }
.diag-detail-line .copy-btn, .diag-detail-msg .copy-btn { top: 50%; transform: translateY(-50%); }
.diag-detail-line { padding-right: 40px; }
.diag-detail-msg { padding-right: 40px; }

.diag-detail-panel .panel-count { text-decoration: none; font-weight: 700; }

/* ---------- the dialog ----------
   Same shape as the pre-visit panel, and the same trap avoided: the display
   property goes on [open] and nowhere else, or the browser's own
   `dialog:not([open]) { display: none }` loses the specificity tie and the
   closed panel sits visible at the top of the page. See portal.css. */

/* The height is definite rather than left to the content, and that is a fix
   rather than a preference. With `height: auto` the body is a flex item of
   `flex: 1 1 0%`, whose zero basis contributes nothing to the container's
   intrinsic height. Some browsers still size the container from the item's
   max-content contribution and some collapse it to the header alone; Dave hit
   the collapsed version on 2026-07-30, a dialog about 90px tall showing one
   line of a long response through its own scrollbar. A definite height gives
   the flex layout a real number to distribute and takes the guess away.

   Wide, too. This is a panel full of URLs, headers and raw JSON, all of which
   are long single lines that wrap badly in a narrow column. */
.diag-dialog {
  width: min(1240px, 95vw);
  max-width: none;
  height: min(90vh, 1000px);
  max-height: 90vh;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: var(--wf-panel);
  color: var(--wf-ink);
  font: 400 15px/1.55 var(--wf-sans);
  box-shadow: 0 24px 70px rgba(59, 46, 37, .34);
  overflow: hidden;
}
.diag-dialog[open] {
  display: flex;
  flex-direction: column;
  animation: diag-in .16s ease-out;
}
.diag-dialog::backdrop {
  background: rgba(43, 33, 26, .52);
  backdrop-filter: blur(2px);
}
@keyframes diag-in {
  from { opacity: 0; transform: translateY(8px) scale(.99); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .diag-dialog[open] { animation: none; }
}

.diag-dialog-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 22px 13px;
  border-bottom: 1px solid var(--wf-hairline);
}
.diag-dialog-head h2 { font-size: 19px; line-height: 1.2; }
.diag-dialog-head .kicker { margin-bottom: 3px; }
/* min-height: 0 is what lets a flex item scroll instead of growing past its
   parent. Without it the item's automatic minimum is its content size and the
   overflow never engages. */
.diag-dialog-body {
  overflow-y: auto;
  padding: 18px 22px 22px;
  flex: 1 1 auto;
  min-height: 0;
}
.diag-dialog-body:focus { outline: none; }

@media (max-width: 720px) {
  .diag-head { flex-direction: column; gap: 12px; }
  .diag-dialog { width: 96vw; max-height: 92vh; }
  .diag-dialog-head { padding: 14px 16px 12px; }
  .diag-dialog-body { padding: 16px 16px 20px; }
  .diag-facts { grid-template-columns: 1fr; }
}

/* ---------- saved filter sets (WIN-49, session 6) ---------- */

/* Same visual weight as the presets row: a quiet second row under the filter
   bar, only rendered for a signed-in SC. */
.diag-saved {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 14px;
  margin: -6px 0 12px;
  padding: 0 12px;
  font-size: 12.5px;
  color: var(--wf-muted);
}
.diag-saved-label { color: var(--wf-muted); }
.diag-saved-item { display: inline-flex; align-items: center; gap: 2px; }
.diag-saved-item form { display: inline; }
.diag-saved-x {
  font: inherit;
  border: 0;
  background: none;
  color: var(--wf-muted);
  cursor: pointer;
  padding: 0 2px;
}
.diag-saved-x:hover { color: var(--wf-body); }
.diag-saved-save { display: inline-flex; align-items: center; gap: 6px; }
.diag-saved-save input[type="text"] {
  font: inherit;
  font-size: 12.5px;
  color: inherit;
  background: var(--wf-panel);
  border: 1px solid var(--wf-hairline-strong);
  border-radius: 4px;
  padding: 3px 6px;
  width: 180px;
}
.diag-saved-save button {
  font: inherit;
  font-size: 12.5px;
  padding: 3px 10px;
  border: 1px solid var(--wf-hairline-strong);
  border-radius: 4px;
  background: var(--wf-panel);
  color: var(--wf-body);
  cursor: pointer;
}
