/* Hub additions to the dashboard stylesheet.
 *
 * Only what a single-server page never needed: choosing between servers, saying that one is
 * unreachable, and the shell around it all. Everything visual comes from dashboard.css, and
 * nothing here restyles anything that file already defines.
 */

/* --- shell -------------------------------------------------------------- */

/* dashboard.css already makes body the grid and styles header, nav.side and main. Only the
   pieces the hub adds are here. */

#crumbs { font-size: 13px; color: var(--text-2); margin-left: 18px; flex: 1; }
#crumbs a { color: var(--text-2); text-decoration: none; }
#crumbs a:hover { color: var(--accent); }
#crumbs .sep { color: var(--text-3); margin: 0 7px; }
#crumbs strong { color: var(--text-0); font-weight: 600; }
#tick { color: var(--text-3); font-size: 11.5px; font-variant-numeric: tabular-nums; min-width: 28px; text-align: right; }
#who { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--text-2); }
.spacer { flex: 1; }

/* No breakpoint of our own that hides the rail.
 *
 * There used to be one here: at 980px it set "nav.side { display: none }". dashboard.css
 * already handles narrow widths properly at 900px, turning the rail into a horizontal
 * scrolling strip of links, and because hub.css loads second this rule beat it. The result
 * was a window between 900 and 980 with no navigation at all, and below 900 the dashboard's
 * own solution was destroyed rather than used. On a tablet, or a browser sharing half a
 * screen, you landed on a page and could not leave it.
 *
 * So the strip is the dashboard's, and the only thing left to say here is what to do with
 * the one element the dashboard never had: the server picker. */
@media (max-width: 900px) {
  .srv-picker {
    display: flex; align-items: center; gap: 10px;
    flex: 0 0 auto; padding: 0 12px 0 2px; margin-right: 4px;
    border-bottom: none; border-right: 1px solid var(--border);
  }
  .srv-picker .meta { display: none; }
  .srv-picker .now { font-size: 13px; max-width: 30vw; }
}

/* --- the server picker in the sidebar ------------------------------------ */

.srv-picker { padding: 10px 14px 14px; border-bottom: 1px solid var(--border); }
.srv-picker .now {
  display: flex; align-items: center; gap: 8px;
  font-weight: 600; font-size: 14px; color: var(--text-0);
}
.srv-picker .now span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.srv-picker .meta { font-size: 11px; color: var(--text-3); margin-top: 3px; line-height: 1.5; }
.srv-picker a.back { font-size: 12px; color: var(--text-3); text-decoration: none; }
.srv-picker a.back:hover { color: var(--accent); }

/* --- states a remote server can be in ------------------------------------ */

.remote-state {
  border: 1px solid var(--border); border-left: 3px solid var(--yellow);
  background: var(--bg-1); border-radius: 10px;
  padding: 14px 16px; margin-bottom: 16px;
}
.remote-state.bad { border-left-color: var(--red); }
.remote-state strong { display: block; margin-bottom: 4px; color: var(--text-0); }
.remote-state p { margin: 0; color: var(--text-2); font-size: 13px; }

/* Secondary text.
 *
 * dashboard.css defines .muted only as td.muted, inside a table. Everywhere else it matched
 * nothing and simply inherited, which made it --text-0: the brightest colour on the page.
 * So every footnote, caption and aside across the hub, 71 of them, rendered as emphasis and
 * shouted louder than the figures they were explaining. */
.muted { color: var(--text-2); }

/* A value nobody measured. The single most important thing this product says. */
.no-reading { color: var(--text-3); font-style: italic; font-size: 0.8em; }

#content.reloading { opacity: .55; transition: opacity .12s; }

/* --- network level: several servers at once ------------------------------ */

.srv-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 12px; }
.srv-card {
  display: block; position: relative; overflow: hidden;
  background: var(--bg-1); border: 1px solid var(--border);
  border-radius: 12px; padding: 13px 15px; color: inherit; text-decoration: none;
}
.srv-card::before { content: ""; position: absolute; inset: 0 auto 0 0; width: 3px; background: var(--text-3); }
.srv-card:hover { border-color: var(--accent); }
.srv-card.sev-ok::before { background: var(--green); }
.srv-card.sev-good::before { background: var(--green); }
.srv-card.sev-warn::before { background: var(--yellow); }
.srv-card.sev-high::before { background: var(--orange); }
.srv-card.sev-crit::before { background: var(--red); }
.srv-card.sev-off { opacity: .55; }
.srv-card .nm { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.srv-card .sub { color: var(--text-3); font-size: 11.5px; margin: 2px 0 10px; }
.srv-card .big { font-size: 25px; line-height: 1; font-variant-numeric: tabular-nums; }
.srv-card .big u { text-decoration: none; font-size: 11px; color: var(--text-2); margin-left: 4px; }
.srv-card .ft {
  display: flex; justify-content: space-between; gap: 8px;
  margin-top: 8px; font-size: 11.5px; color: var(--text-2);
  font-variant-numeric: tabular-nums;
}

/* --- vocabulary the helpers emit that dashboard.css does not define ------- */

.faint { color: var(--text-3); }
.nowrap { white-space: nowrap; }

/* Views written before the stylesheet swap still reach for these three. Defined against
   the adopted tokens rather than left unstyled, so nothing renders as naked text while
   they are converted one by one. */
.notice {
  border: 1px solid var(--border); border-left: 3px solid var(--yellow);
  background: var(--bg-1); border-radius: 10px; padding: 13px 16px;
  margin-bottom: 14px; font-size: 13px;
}
.notice.bad { border-left-color: var(--red); }
.notice strong { display: block; margin-bottom: 4px; color: var(--text-0); }
.notice p { margin: 4px 0 0; color: var(--text-2); }

.tree > summary { cursor: pointer; color: var(--text-1); font-size: 13px; padding: 3px 0; }
.tree > summary:hover { color: var(--accent); }
.tree-list { list-style: none; margin: 4px 0 4px 14px; padding: 0; }

pre.console {
  margin: 0; padding: 11px 13px;
  background: var(--bg-0); border: 1px solid var(--border); border-radius: 8px;
  font-size: 11.5px; line-height: 1.5; max-height: 480px; overflow: auto;
  white-space: pre-wrap; word-break: break-word;
}
pre.console .sev-warn { color: var(--yellow); }
pre.console .sev-crit { color: var(--red); }
pre.console .gutter { color: var(--text-3); }

/* Categorical colours for comparing servers. Deliberately not the severity palette: a line
   on a comparison chart means "this server", never "this is bad". */
:root {
  --series-1: #7289da; --series-2: #46b3a5; --series-3: #c586c0;
  --series-4: #d9a84e; --series-5: #6ba6e8; --series-6: #cf6b6b;
}

select {
  background: var(--bg-2); color: var(--text-1);
  border: 1px solid var(--border); border-radius: 7px;
  padding: 5px 9px; font: inherit; font-size: 12.5px;
}
select:hover { border-color: var(--text-3); }
select:focus { outline: none; border-color: var(--accent); }

/* The dashboard sizes its own charts through ".chart-card canvas". These are SVG, so
   they need the same margin stated separately or the title sits on top of the plot. */
/* Height comes from the viewBox aspect ratio, not from CSS. Fixing the height and letting
   the width run free is what forces an SVG to scale its two axes independently, and that is
   what was stretching every axis label into something thin and wide. */
.chart { width: 100%; height: auto; display: block; margin-top: 10px; }

/* dashboard.css styles .badge only inside the sidebar, so UI.badge came out as bare text
   anywhere else. Same shape as .pill, outlined rather than filled, so the two read as
   different kinds of label rather than as the same one rendered inconsistently. */
.badge {
  display: inline-block; padding: 1px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 600; letter-spacing: .01em;
  border: 1px solid var(--border); color: var(--text-2);
}
.badge.green { border-color: rgba(46, 204, 113, .45); color: var(--green); }
.badge.yellow { border-color: rgba(241, 196, 15, .45); color: var(--yellow); }
.badge.orange { border-color: rgba(230, 126, 34, .45); color: var(--orange); }
.badge.red { border-color: rgba(231, 76, 60, .45); color: var(--red); }

/* --- severity as a bare class ------------------------------------------- */

/* dashboard.css defines these only inside other selectors, so a figure coloured by
   UI.stat came out grey. Colour still carries exactly one meaning: severity. */
.green  { color: var(--green); }
.yellow { color: var(--yellow); }
.orange { color: var(--orange); }
.red    { color: var(--red); }

/* --- layout vocabulary the views still use ------------------------------- */

/* Defined here against the adopted tokens rather than rewritten in eight view files. The
   views are correct; they were written against a stylesheet that was swapped under them,
   and a class is cheaper to add than eight files are to edit. */

.cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 14px; align-items: start; }
.grid2 { display: grid; grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr); gap: 14px; align-items: start; }
@media (max-width: 1100px) { .grid2 { grid-template-columns: 1fr; } }

.findings { list-style: none; margin: 0; padding: 0; }
.findings li { border-left: 2px solid var(--border); padding: 7px 0 7px 10px; margin-bottom: 3px; font-size: 13px; }
.findings li.sev-warn { border-left-color: var(--yellow); }
.findings li.sev-high { border-left-color: var(--orange); }
.findings li.sev-crit { border-left-color: var(--red); }
.findings .what { font-weight: 550; color: var(--text-0); }
.findings .why { color: var(--text-2); font-size: 12.5px; }
.findings .conf { color: var(--text-3); font-size: 11.5px; margin-top: 2px; }

.chart-block { margin-bottom: 10px; }

/* --- administration, forms and the shell's own furniture ------------------
 *
 * dashboard.css is a single server's page. It never had an admin section, a form, a page
 * heading or a server card, so none of these existed and every one of them was rendering as
 * unstyled inherited text. That is why the admin pages looked like a plain HTML document
 * with a dark background: not a missing design, a design that matched nothing.
 *
 * Everything below reuses dashboard.css's own tokens, so it reads as the same product. */

.page-head { display: flex; align-items: baseline; gap: 14px; margin-bottom: 18px; }
/* dashboard.css defines .sub only inside a card, so a subtitle beside a page heading
   inherited --text-0 and the aside was brighter than the heading it explained. */
.page-head .sub { color: var(--text-3); font-size: 12.5px; font-weight: 400; }
.page-head h1 { font-size: 21px; font-weight: 650; margin: 0; }
.page-head .spacer { flex: 1; }

/* The same box as .card, under the name the admin pages and the overview already use. */
.panel {
  background: var(--bg-1); border: 1px solid var(--border);
  border-radius: 12px; padding: 16px 18px; margin-bottom: 14px;
}
.panel h3 { font-size: 14px; font-weight: 600; margin: 0 0 10px; }
.panel > p:last-child, .panel > div:last-child { margin-bottom: 0; }

.admin-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
@media (max-width: 1100px) { .admin-grid { grid-template-columns: 1fr; } }

/* A definition list read as label-then-value, not as an indented blob. */
dl.kv { display: grid; grid-template-columns: auto 1fr; gap: 5px 14px; margin: 0; font-size: 12.5px; }
dl.kv dt { color: var(--text-2); }
dl.kv dd { margin: 0; color: var(--text-0); font-variant-numeric: tabular-nums; }

/* --- forms ---------------------------------------------------------------
 * Nothing styled an input anywhere in either stylesheet, so every text field on the hub was
 * a white browser default box on a near-black page. */

input[type="text"], input[type="number"], input[type="search"], input:not([type]), textarea {
  background: var(--bg-2); color: var(--text-0);
  border: 1px solid var(--border); border-radius: 7px;
  padding: 6px 9px; font: inherit; font-size: 12.5px;
  min-width: 0;
}
input::placeholder, textarea::placeholder { color: var(--text-3); }
input:focus, textarea:focus, select:focus { outline: none; border-color: var(--accent); }
input:disabled, textarea:disabled, select:disabled { opacity: .5; cursor: not-allowed; }

.form-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.form-row input, .form-row select { flex: 1 1 140px; }
.form-row button { flex: 0 0 auto; }

/* A token or a one-time URL. Monospace so it can be read back over a call, wrapping so it is
 * never truncated into something that looks complete but is not, and marked as a secret so
 * nobody pastes it somewhere public by mistake. */
.secret {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 12px; line-height: 1.6; word-break: break-all; user-select: all;
  background: var(--bg-2); border: 1px dashed var(--yellow);
  border-radius: 8px; padding: 9px 11px; margin-top: 8px; color: var(--text-0);
}

/* --- small pieces --------------------------------------------------------- */

.tag {
  display: inline-block; padding: 1px 7px; border-radius: 6px;
  background: var(--bg-2); border: 1px solid var(--border);
  font-size: 11px; color: var(--text-2); white-space: nowrap;
}
.tag.role { border-color: rgba(114, 137, 218, .45); color: var(--accent); }

button.danger { border-color: rgba(231, 76, 60, .5); color: var(--red); }
button.danger:hover:not(:disabled) { background: rgba(231, 76, 60, .12); }

/* A table cell holding a name rather than a number: left aligned, not tabular. */
td.txt, th.txt { font-variant-numeric: normal; text-align: left; }

/* The signed-out and no-access pages, which are the only full-page centred things here. */
.center { max-width: 460px; margin: 12vh auto 0; text-align: center; }
.center h1 { font-size: 24px; margin-bottom: 8px; }
.center p { color: var(--text-2); font-size: 13.5px; margin-bottom: 18px; }

/* Bare severity words used as a class on their own, outside the .sev- prefix.
   .warn was the one missing from this set, which is the kind of gap that only shows up
   when something is actually warning. */
.good, .ok { color: var(--green); }
.warn { color: var(--yellow); }
.high { color: var(--orange); }
.crit { color: var(--red); }

/* dashboard.css defines .right only inside its header, so every right-aligned figure
   elsewhere, eight of them in Administration alone, quietly computed text-align: start
   and columns of numbers did not line up. */
td.right, th.right, .right { text-align: right; }
header .right { text-align: initial; }

/* --- the world map page and the events timeline ---------------------------
 *
 * Neither existed in the single-server dashboard, so like the admin section above they were
 * emitting class names that matched nothing at all. */

section.group { margin-bottom: 22px; }
section.group > h2 { font-size: 13px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-3); margin: 0 0 10px; }

.row { display: flex; align-items: center; gap: 10px; }

.worldmap { width: 100%; height: auto; display: block; background: var(--bg-2);
  border: 1px solid var(--border); border-radius: 12px; padding: 8px; }
/* Stroke only. A country carrying players gets its fill from an inline attribute computed
   per value, and a CSS fill here would beat that attribute and flatten the whole map to one
   colour, which is the opposite of what the map is for. Only the countries with no data get
   a fill from CSS. */
.worldmap path { stroke: var(--border); stroke-width: .4; }
.worldmap path.country:not(.has) { fill: var(--bg-0); }
/* A country too small to draw still gets a dot, so nobody concludes a region is empty when
   the truth is that it is two pixels wide. */
.worldmap .pin { fill: var(--accent); fill-opacity: .75; stroke: var(--bg-0); stroke-width: .3; }
.legend { font-size: 11px; color: var(--text-3); }

/* The per-country ranking under the map. */
ol.bars { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
ol.bars li { display: grid; grid-template-columns: 22px 1fr 3fr 52px 52px; align-items: center;
  gap: 9px; font-size: 12.5px; }
ol.bars .flag { font-size: 15px; line-height: 1; }
ol.bars .bar { display: block; height: 7px; border-radius: 4px; background: var(--bg-2); overflow: hidden; }
ol.bars .bar i { display: block; height: 100%; background: var(--accent); }
ol.bars .n, ol.bars .p { text-align: right; font-variant-numeric: tabular-nums; }
ol.bars .p { color: var(--text-3); }
@media (max-width: 720px) { ol.bars li { grid-template-columns: 22px 1fr 2fr 44px; }
  ol.bars .p { display: none; } }

/* The events timeline, shared by the events page and the alerts view. */
ul.events { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; }
ul.events li { display: grid; grid-template-columns: 62px 1fr; gap: 10px; align-items: baseline;
  padding: 7px 10px; border-left: 2px solid var(--border); background: var(--bg-2);
  border-radius: 0 7px 7px 0; }
ul.events li time { color: var(--text-3); font-size: 11px; font-variant-numeric: tabular-nums; }
ul.events .what { font-size: 12.5px; color: var(--text-0); }
ul.events .where { font-size: 11.5px; color: var(--text-2); margin-top: 1px; }
ul.events .detail { font-size: 11px; color: var(--text-3); margin-top: 2px; word-break: break-word; }
/* Three spellings, because three exist. The views abbreviate (sev-warn), the plugin's own
   pages shout (sev-WARNING), and the events API sends the full word in lower case
   (sev-warning). Only the first two were listed, so on the events page, where colour is the
   only signal of severity, a spike burst was the same grey as a config reload. */
ul.events li.sev-warn, ul.events li.sev-WARNING, ul.events li.sev-warning,
ul.events li.sev-medium, ul.events li.sev-MEDIUM { border-left-color: var(--yellow); }
ul.events li.sev-high, ul.events li.sev-HIGH { border-left-color: var(--orange); }
ul.events li.sev-crit, ul.events li.sev-CRITICAL, ul.events li.sev-critical
  { border-left-color: var(--red); }
ul.events li.sev-ok, ul.events li.sev-OK, ul.events li.sev-low, ul.events li.sev-LOW
  { border-left-color: var(--green); }
ul.events li.sev-info, ul.events li.sev-INFO { border-left-color: var(--border); }

.count { font-variant-numeric: tabular-nums; color: var(--text-2); }

/* A card's right-hand note. It used to borrow .link, which dashboard.css defines only inside
   the sidebar, plus three inline styles to undo what that class would have done. */
.card .label .aside { float: right; text-transform: none; letter-spacing: 0;
  font-weight: 400; color: var(--text-3); font-size: 11px; }

/* The dashboard sizes a unit inside a figure with .unit; these pages write <small>, which
   without a rule inherits the 28px figure and reads as part of the number. */
.card .value small { color: var(--text-3); font-size: 14px; font-weight: 500; margin-left: 5px; letter-spacing: 0; }

/* A card that spans its whole row. UI.card takes {wide: true} for this and nine views ask
   for it; the class it implies simply never existed, so a full-width history chart was
   squeezed into one column of .cols. */
.card.wide { grid-column: 1 / -1; }

/* --- narrow widths ---------------------------------------------------------
 *
 * Every rule here comes from a measured failure at 390, 768, 960 or 1280, not from taste.
 * The two at the top lose data rather than merely looking tight, which is the difference
 * between untidy and wrong. */

/* A table inside a .card is clipped with no way to scroll, because dashboard.css gives .card
   overflow:hidden and tables are width:100%. Measured: the alerts table needs 567px and gets
   348 at phone width, so COOLING DOWN, WEBHOOK and STATE are simply not on the page. The
   alerts view then shows you the rules while hiding whether any of them is firing. */
@media (max-width: 1000px) {
  .card { overflow-x: auto; overflow-y: hidden; }
  .card > table { width: max-content; min-width: 100%; }
  /* A .panel has no overflow rule at all, so its tables compress instead of clipping. The
     audit log wants 666px and gets 356: the result column lands at ~30px and wraps a request
     id three characters to a line. Still wrong at 960, hence the wider breakpoint. */
  .panel { overflow-x: auto; }
  .panel > table { width: max-content; min-width: 100%; }
}

/* The heading and its blurb sat side by side in 198px and 150px on the map page, both
   wrapping mid-word. */
.page-head { flex-wrap: wrap; }

/* #who overflowed the 56px header and clipped the sign-out button to "Sig / ou". */
@media (max-width: 560px) {
  #who { gap: 6px; font-size: 12px; }
  #who .muted { max-width: 34vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  header { padding: 0 10px; }
}

/* Touch targets. Measured at 21px for the per-grant buttons on the people page, 24px for
   .btn.small, 25px for a tree row: all well under a fingertip. Only on pointers that are
   actually coarse, so a mouse still gets the compact spacing. */
@media (pointer: coarse) {
  button, .btn, .tree > summary { min-height: 34px; }
  button.small, .btn.small { min-height: 32px; padding-left: 12px; padding-right: 12px; }
  input, select, textarea { min-height: 34px; }
}

/* The server name is the only thing on a phone telling you which server you are looking at,
   and 30vw truncated it to 117px. In the horizontal strip it can have the room. */
@media (max-width: 900px) {
  .srv-picker .now { max-width: 46vw; }
}

/* .cols asks for a 320px minimum, which overflows below about 360px wide. */
.cols { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); }

/* --- figure weight ---------------------------------------------------------
 *
 * dashboard.css sets ".card .value" to 34px/700, and that is right where the original uses
 * it: the four-up strip at the top of a page, one number each, nothing competing. The hub
 * routes every figure through the same helper, so three numbers side by side inside a card
 * came out at 34px bold as well and shouted over everything around them. The headline size
 * stays where it belongs and everything else steps down. */
.card .value { font-size: 21px; font-weight: 600; letter-spacing: -.01em; }
/* The 34px headline is for a strip of figures that owns the top of a page. */
.grid.stats .card .value { font-size: 34px; font-weight: 700; letter-spacing: -.02em; }
/* A strip nested inside a card is not that. Three figures side by side within a panel are
   a breakdown, and at 34px bold they shouted over the panel's own heading and over the
   charts above them. The original never puts a headline figure inside another card. */
.card .grid.stats .card .value,
.panel .grid.stats .card .value {
  font-size: 19px; font-weight: 600; letter-spacing: -.01em;
}
.card .grid.stats .card, .panel .grid.stats .card { min-height: 0; padding: 12px 14px; }
.card .grid.stats, .panel .grid.stats { gap: 10px; margin-bottom: 0; }
.card .value small, .card .value .unit { font-size: 12.5px; }
.grid.stats .card .value small, .grid.stats .card .value .unit { font-size: 14px; }

/* A chart card's corner value is a reading, not a headline. */
.chart-card .title-row .value { font-size: 12.5px; font-weight: 500; color: var(--text-2); }

/* A grid or flex child defaults to min-width:auto, which refuses to shrink below its own
   content. Giving a card or panel an overflow-x above therefore made the box itself grow to
   fit a 1285px table instead of scrolling inside 390px, and the whole page gained 104px of
   horizontal scroll: the clipping was fixed and replaced with a shove. min-width:0 is what
   lets them actually be narrower than what they contain. */
.card, .panel, .chart-card, .grid > *, .cols > *, .grid2 > *, .admin-grid > *, .srv-cards > * {
  min-width: 0;
}
.card, .panel { max-width: 100%; }
