/*
 * Umbrella LOG's own overrides. Loaded after the Modernize stylesheet, and the
 * only CSS in the tree we wrote.
 *
 * It exists so the vendor stylesheet stays byte-identical to the one that ships
 * with the theme. Every failure mode of this template is silent (a page simply
 * renders as plain Bootstrap), so a local edit inside styles.css would survive
 * exactly until the next theme update and then be lost without an error.
 */

/*
 * Density.
 *
 * Modernize sizes everything in rem, including --bs-body-font-size (0.875rem)
 * and the whole .fs-* scale, so one change to the root size scales the entire
 * interface proportionally rather than leaving headings and tables out of step.
 * 14px against the browser's default 16px is 87.5%.
 *
 * The .fs-7 and above steps use calc(... + Nvw), so they shrink less than the
 * rest. That is the right way round: those are page headings, which should not
 * collapse into the body text just because the tables got denser.
 */
html {
  font-size: 14px;
}

/*
 * Table rows carry the density that actually matters here -- a trace result is
 * read 500 rows at a time, and the vertical padding, not the type size, is what
 * decides how many of them fit. The cells still hold two stacked lines in the
 * live stream, so this is trimmed rather than removed.
 */
.table > :not(caption) > * > * {
  padding-top: 0.4rem;
  padding-bottom: 0.4rem;
}

/*
 * The log tables are dense grids of addresses and ports, where a digit that is
 * one pixel narrower than its neighbour makes a column impossible to scan down.
 * Tabular figures keep every digit the same width without changing the face.
 */
.table td,
.table th {
  font-variant-numeric: tabular-nums;
}

/*
 * Vertical rhythm.
 *
 * Modernize spaces its cards for a marketing dashboard with six figures on it.
 * This application's pages are dense -- twelve panels, or five hundred log rows
 * -- and the theme's 30px gutters and 30px card padding push the third row of
 * anything below the fold on a laptop. These are the theme's own variables and
 * classes, retuned; nothing is restructured.
 */
:root {
  --bs-gutter-x: 1.25rem;
  --bs-gutter-y: 0;
}

.card {
  margin-bottom: 1.25rem;
}

.card-body {
  padding: 1.1rem;
}

/* The body wrapper's own padding is set for the theme's wide cards. */
.body-wrapper > .container-fluid {
  padding: 1.25rem 1.25rem 0;
}

/*
 * Modernize's topbar is 70px tall and the sidebar header matches it. Nothing on
 * this appliance's topbar needs that: it holds a product name and one account
 * control. Both are trimmed together -- they are separate rules in styles.css
 * and changing one leaves the sidebar logo hanging.
 */
.topbar .navbar {
  min-height: 58px;
}

.topbar .navbar .navbar-nav .nav-item .nav-link {
  line-height: 58px;
  height: 58px;
}

/*
 * min-height as well as height: the theme's own rule is
 * `.brand-logo{min-height:70px;padding:0 24px}`, and a bare `height:58px`
 * loses to it -- the box stays 70px tall and the sidebar's brand sits 12px
 * below the topbar it is supposed to line up with. Nothing reports this; the
 * page simply looks a little off.
 */
.left-sidebar .brand-logo {
  height: 58px;
  min-height: 58px;
}

[data-layout="vertical"] .body-wrapper {
  padding-top: 58px;
}

/*
 * Card titles. The theme's h4/h5 inside a card are sized for a page with three
 * cards on it; at twelve they read as a wall of headings.
 */
.card-title {
  font-size: 1rem;
  line-height: 1.35;
}

.card-subtitle {
  font-size: 0.75rem;
}

/*
 * Fixed-size round wells. Modernize ships .round-48 and .round-20 but not the
 * two sizes this application's denser cards want.
 */
.round-40 {
  width: 40px;
  height: 40px;
}

.round-32 {
  width: 32px;
  height: 32px;
}

/*
 * A panel swapped in by the dashboard's refresh should not flash. Fifteen
 * seconds apart, an abrupt repaint reads as the page having reloaded -- which
 * is precisely the impression the refresh exists to avoid.
 */
[id^="dash-"] .card {
  transition: opacity 0.15s ease-in-out;
}

/* ------------------------------------------------------------------ *
 * Success green
 *
 * Modernize's success colour is #13deb9 -- a bright blue-green that reads as
 * mint rather than as green, and that at 1.7:1 against white is not legible as
 * text at all. It is the colour of every "answering", every healthy badge and
 * every availability figure in this application, so it is worth getting right.
 *
 * Replaced with a natural leaf green. Two values rather than one, because the
 * same green cannot serve both themes: on white a colour needs to be dark
 * enough to read (4.1:1 here, against 1.7:1 before), and on the dark theme's
 * #202936 that same dark green disappears, so the lighter #4cbb17 is used
 * there instead. Same hue in both; only the depth changes with the ground.
 *
 * These are the theme's own variables, so every .text-success, .bg-success,
 * .bg-success-subtle, .alert-success and .badge in the interface follows
 * without a single template being touched. The two button classes are listed
 * separately only because the theme compiled their colours as literals.
 * ------------------------------------------------------------------ */

:root,
[data-bs-theme="light"] {
  --bs-success: #2f8f1e;
  --bs-success-rgb: 47, 143, 30;
  --bs-green: #2f8f1e;
  --bs-success-bg-subtle: #eaf6e4;
  --bs-success-border-subtle: #bce2ab;
  /*
   * The theme ships `--bs-success-text-emphasis:shade-color(#13deb9, 60%)`:
   * an unresolved SCSS function that leaked into the compiled stylesheet, so
   * the value is invalid and every .alert-success and .text-success-emphasis
   * has been falling back to inherited ink. Given a real colour here.
   */
  --bs-success-text-emphasis: #1e5f13;
  --bs-form-valid-color: #2f8f1e;
  --bs-form-valid-border-color: #2f8f1e;
}

[data-bs-theme="dark"] {
  --bs-success: #4cbb17;
  --bs-success-rgb: 76, 187, 23;
  --bs-green: #4cbb17;
  --bs-success-bg-subtle: #22331a;
  --bs-success-border-subtle: #3c6f21;
  --bs-success-text-emphasis: #93d96b;
  --bs-form-valid-color: #4cbb17;
  --bs-form-valid-border-color: #4cbb17;
}

/*
 * .btn-success and .btn-outline-success set their own --bs-btn-* values from
 * literal hex in styles.css, so they do not follow --bs-success and have to be
 * restated. The hover and active steps are the base darkened by roughly 15%
 * and 22%, which is the relationship the theme's own buttons use.
 */
.btn-success {
  --bs-btn-bg: #2f8f1e;
  --bs-btn-border-color: #2f8f1e;
  --bs-btn-hover-bg: #287a1a;
  --bs-btn-hover-border-color: #256f18;
  --bs-btn-active-bg: #256f18;
  --bs-btn-active-border-color: #216416;
  --bs-btn-disabled-bg: #2f8f1e;
  --bs-btn-disabled-border-color: #2f8f1e;
  --bs-btn-focus-shadow-rgb: 47, 143, 30;
}

.btn-outline-success {
  --bs-btn-color: #2f8f1e;
  --bs-btn-border-color: #2f8f1e;
  --bs-btn-hover-bg: #2f8f1e;
  --bs-btn-hover-border-color: #2f8f1e;
  --bs-btn-active-bg: #2f8f1e;
  --bs-btn-active-border-color: #2f8f1e;
  --bs-btn-disabled-color: #2f8f1e;
  --bs-btn-disabled-border-color: #2f8f1e;
  --bs-btn-focus-shadow-rgb: 47, 143, 30;
}

[data-bs-theme="dark"] .btn-success {
  --bs-btn-bg: #4cbb17;
  --bs-btn-border-color: #4cbb17;
  --bs-btn-hover-bg: #419f14;
  --bs-btn-hover-border-color: #3c9312;
  --bs-btn-active-bg: #3c9312;
  --bs-btn-active-border-color: #368311;
  --bs-btn-disabled-bg: #4cbb17;
  --bs-btn-disabled-border-color: #4cbb17;
  --bs-btn-focus-shadow-rgb: 76, 187, 23;
}

[data-bs-theme="dark"] .btn-outline-success {
  --bs-btn-color: #4cbb17;
  --bs-btn-border-color: #4cbb17;
  --bs-btn-hover-bg: #4cbb17;
  --bs-btn-hover-border-color: #4cbb17;
  --bs-btn-active-bg: #4cbb17;
  --bs-btn-active-border-color: #4cbb17;
  --bs-btn-disabled-color: #4cbb17;
  --bs-btn-disabled-border-color: #4cbb17;
  --bs-btn-focus-shadow-rgb: 76, 187, 23;
}

/* Compiled as a literal too, and used by nothing here yet -- restated so that
   the first page to raise a toast does not raise a mint-coloured one. */
.toast-success {
  background-color: #2f8f1e !important;
}

[data-bs-theme="dark"] .toast-success {
  background-color: #4cbb17 !important;
}

/* ------------------------------------------------------------------ *
 * Danger red and warning amber
 *
 * The same fault as the success green, found later and left alone until now
 * because it was a separate decision to make: the theme's #fa896b and #ffae1f
 * are pastels drawn for large fills, and as text on white they measure
 * 2.37:1 and 1.85:1. They are the colour of every alert row, every "Broken"
 * and every threshold figure in this application.
 *
 * Only the light theme is repainted, and that is the whole point of measuring
 * rather than assuming: on the dark theme's #202936 those same two pastels
 * already give 6.18:1 and 7.91:1, so deepening them there would make the
 * interface worse. Light gets a deep red and a deep amber -- the two values
 * the PingTrace verdict badge had already been using on its own, so the
 * product now has one red and one amber instead of two of each.
 *
 * The subtle backgrounds and borders are deliberately NOT retinted. The
 * measured fault is the ink, not the ground, and the warm tints still sit
 * under the deeper inks as the same family of colour.
 * ------------------------------------------------------------------ */

:root,
[data-bs-theme="light"] {
  --bs-danger: #c0392b;          /* 5.44:1 on white, was 2.37:1 */
  --bs-danger-rgb: 192, 57, 43;
  --bs-red: #c0392b;
  --bs-warning: #a06800;         /* 4.69:1 on white, was 1.85:1 */
  --bs-warning-rgb: 160, 104, 0;
  --bs-yellow: #a06800;

  /*
   * Both of these ship as `shade-color(...)` -- the same unresolved SCSS call
   * that broke --bs-success-text-emphasis, so every .alert-danger and
   * .alert-warning in this application has been falling back to inherited ink
   * and none of them has been the colour it was meant to be. There are 32 of
   * them. Given real values: a 40% shade of the new base, which is the
   * relationship the theme's own emphasis colours use.
   */
  --bs-danger-text-emphasis: #73221a;   /* 9.66:1 on #fbf2ef */
  --bs-warning-text-emphasis: #603e00;  /* 8.88:1 on #fef5e5 */

  --bs-form-invalid-color: #c0392b;
  --bs-form-invalid-border-color: #c0392b;
}

/*
 * Dark has to be restated even though it is not being changed, and this is the
 * trap: the block above is `:root, [data-bs-theme="light"]`, and `:root`
 * matches the html element whatever the theme is. This file loads after the
 * theme's stylesheet at equal specificity, so without the block below the
 * light theme's deep ink follows the interface onto the dark ground -- and it
 * measures 1.09:1 on .alert-danger there, against 6.18:1 before. Measured in
 * a browser, not assumed; nothing about the page looks wrong until you read a
 * word on it.
 *
 * So these are the theme's own dark values, restored deliberately.
 */
[data-bs-theme="dark"] {
  --bs-danger: #fa896b;          /* 6.18:1 on #202936 -- already legible */
  --bs-danger-rgb: 250, 137, 107;
  --bs-red: #fa896b;
  --bs-warning: #ffae1f;         /* 7.91:1 on #202936 */
  --bs-warning-rgb: 255, 174, 31;
  --bs-yellow: #ffae1f;
  --bs-danger-text-emphasis: #fcb8a6;
  --bs-warning-text-emphasis: #ffce79;
  --bs-form-invalid-color: #fa896b;
  --bs-form-invalid-border-color: #fa896b;
}


/*
 * Solid fills carry white text, so they need a dark fill in BOTH themes --
 * white on the theme's #fa896b is 2.37:1 wherever it appears. Nothing in this
 * application raises one of these yet; they are restated so the first page
 * that does gets a legible button rather than an illegible pastel one.
 */
.btn-danger {
  --bs-btn-bg: #c0392b;
  --bs-btn-border-color: #c0392b;
  --bs-btn-hover-bg: #a33025;
  --bs-btn-hover-border-color: #962c22;
  --bs-btn-active-bg: #962c22;
  --bs-btn-active-border-color: #8a291f;
  --bs-btn-disabled-bg: #c0392b;
  --bs-btn-disabled-border-color: #c0392b;
  --bs-btn-focus-shadow-rgb: 192, 57, 43;
}

.btn-warning {
  --bs-btn-bg: #a06800;
  --bs-btn-border-color: #a06800;
  --bs-btn-hover-bg: #885800;
  --bs-btn-hover-border-color: #7d5100;
  --bs-btn-active-bg: #7d5100;
  --bs-btn-active-border-color: #734b00;
  --bs-btn-disabled-bg: #a06800;
  --bs-btn-disabled-border-color: #a06800;
  --bs-btn-focus-shadow-rgb: 160, 104, 0;
}

/*
 * Outline buttons are the opposite case: their colour is ink on the page's own
 * ground, so it has to follow the ground. Deep in light, and the theme's
 * pastel restored in dark, where it is already 6.18:1 and 7.91:1.
 */
.btn-outline-danger {
  --bs-btn-color: #c0392b;
  --bs-btn-border-color: #c0392b;
  --bs-btn-hover-bg: #c0392b;
  --bs-btn-hover-border-color: #c0392b;
  --bs-btn-active-bg: #c0392b;
  --bs-btn-active-border-color: #c0392b;
  --bs-btn-disabled-color: #c0392b;
  --bs-btn-disabled-border-color: #c0392b;
  --bs-btn-focus-shadow-rgb: 192, 57, 43;
}

.btn-outline-warning {
  --bs-btn-color: #a06800;
  --bs-btn-border-color: #a06800;
  --bs-btn-hover-bg: #a06800;
  --bs-btn-hover-border-color: #a06800;
  --bs-btn-active-bg: #a06800;
  --bs-btn-active-border-color: #a06800;
  --bs-btn-disabled-color: #a06800;
  --bs-btn-disabled-border-color: #a06800;
  --bs-btn-focus-shadow-rgb: 160, 104, 0;
}

[data-bs-theme="dark"] .btn-outline-danger {
  --bs-btn-color: #fa896b;
  --bs-btn-border-color: #fa896b;
  --bs-btn-hover-bg: #fa896b;
  --bs-btn-hover-border-color: #fa896b;
  --bs-btn-active-bg: #fa896b;
  --bs-btn-active-border-color: #fa896b;
  --bs-btn-disabled-color: #fa896b;
  --bs-btn-disabled-border-color: #fa896b;
  --bs-btn-focus-shadow-rgb: 250, 137, 107;
}

[data-bs-theme="dark"] .btn-outline-warning {
  --bs-btn-color: #ffae1f;
  --bs-btn-border-color: #ffae1f;
  --bs-btn-hover-bg: #ffae1f;
  --bs-btn-hover-border-color: #ffae1f;
  --bs-btn-active-bg: #ffae1f;
  --bs-btn-active-border-color: #ffae1f;
  --bs-btn-disabled-color: #ffae1f;
  --bs-btn-disabled-border-color: #ffae1f;
  --bs-btn-focus-shadow-rgb: 255, 174, 31;
}

/* ------------------------------------------------------------------ *
 * Primary
 *
 * The same fault as danger and warning above, in the colour the product is
 * built out of. Cyan_Theme's --bs-primary is #01c0c8, a bright cyan drawn for
 * large fills, and measured in a browser rather than assumed it is:
 *
 *     light   text-primary on a card ........ 2.00:1
 *             .badge.bg-primary-subtle ...... 1.85:1
 *             .btn-outline-primary .......... 2.95:1
 *     both    white label on .btn-primary ... 2.24:1
 *     dark    text-primary on #202936 ....... 6.55:1  (already fine)
 *
 * The last one is the reason this is not cosmetic: .btn-primary is Search,
 * Apply, Save and Add -- the button on nearly every page -- and its white
 * label was illegible in *both* themes, which is why the fill below is
 * deepened in both rather than only in light.
 *
 * #01757a is the same hue (182deg) and the same saturation as the theme's
 * cyan, three steps darker: it still reads as this product's colour, it is
 * just legible. Against white it measures 5.49:1, against the 10% cyan wash
 * behind a subtle badge 5.03:1, and white on it 5.49:1 -- one value that
 * serves as ink, as a badge, and as a fill.
 *
 * Dark keeps the theme's own #01c0c8: on #202936 it is already 6.55:1, and
 * deepening ink against a dark ground would make it worse. Same finding as
 * danger and warning, same conclusion.
 * ------------------------------------------------------------------ */

/*
 * These have to be written the long way, and that is a third trap on top of
 * the two the danger block documents.
 *
 * danger and warning are overridable from a plain `:root` because the theme
 * defines them once, at `:root`. --bs-primary is not: every colour theme
 * redefines it in a block selected as
 * `[data-bs-theme=light][data-color-theme=Cyan_Theme]:root`, which is
 * specificity (0,3,0) against a bare `:root`'s (0,1,0). Loading later does not
 * help -- source order only decides ties. Written as `:root, [data-bs-theme=
 * "light"]` this whole section silently did nothing: the file parsed, the
 * declarations were there in devtools, greyed out, and every measurement came
 * back exactly as it had been before.
 *
 * So it is matched at the theme's own shape and specificity. Naming the theme
 * is deliberate rather than a shortcut: the deep teal below is derived from
 * Cyan's own cyan, and if anyone ever changes ColorTheme in umbrella.init.js
 * these rules should stop applying rather than staple a teal onto a different
 * palette. It also means the light and dark values cannot leak into each
 * other the way the danger block warns about -- each selector names its theme
 * -- but both are still stated, because a reader should not have to work that
 * out to be sure.
 */
[data-bs-theme="light"][data-color-theme="Cyan_Theme"]:root {
  --bs-primary: #01757a;         /* 5.49:1 on white, was 2.24:1 */
  --bs-primary-rgb: 1, 117, 122;
  --bs-cyan: #01757a;

  /*
   * Ships as `shade-color(#5d87ff, 60%)` -- an unresolved SCSS call, and of
   * the Blue theme's colour rather than this one, so every .alert-primary has
   * been falling back to inherited ink and none has been the intended colour.
   * The same fault the success and danger emphasis colours had. Given a real
   * value: a 40% shade of the base above, the relationship the theme's own
   * emphasis colours use.
   */
  --bs-primary-text-emphasis: #014649;  /* 9.75:1 on the subtle ground */
}

[data-bs-theme="dark"][data-color-theme="Cyan_Theme"]:root {
  --bs-primary: #01c0c8;         /* 6.55:1 on #202936 -- already legible */
  --bs-primary-rgb: 1, 192, 200;
  --bs-cyan: #01c0c8;
  --bs-primary-text-emphasis: #4dd3d8;  /* 6.82:1 on the subtle ground */
}

/*
 * The solid button carries a white label, so it needs the deep fill in BOTH
 * themes -- white on the theme's #01c0c8 is 2.24:1 wherever it appears, and
 * unlike danger and warning this button is on nearly every page of the
 * application. Restated here rather than left to --bs-primary because the
 * theme sets .btn-primary's fill with its own literal hex, at a specificity
 * a bare `.btn-primary` cannot reach -- see the note on the variables above.
 */
[data-bs-theme="light"][data-color-theme="Cyan_Theme"]:root .btn-primary,
[data-bs-theme="dark"][data-color-theme="Cyan_Theme"]:root .btn-primary {
  --bs-btn-bg: #01757a;
  --bs-btn-border-color: #01757a;
  --bs-btn-hover-bg: #016368;
  --bs-btn-hover-border-color: #015b5f;
  --bs-btn-active-bg: #015b5f;
  --bs-btn-active-border-color: #015b5f;
  --bs-btn-disabled-bg: #01757a;
  --bs-btn-disabled-border-color: #01757a;
  --bs-btn-focus-shadow-rgb: 1, 117, 122;
}

/*
 * The outline button is a second bug wearing the first one's clothes: the
 * theme writes its colour as a literal #5d87ff, and the Cyan block overrides
 * only .btn-primary. So every outline button in this application has been the
 * *Blue* theme's blue -- 2.95:1, and not even this product's colour. The
 * "Trace", "Pause" and "Export" buttons are all this. Given the brand colour
 * it should have had, deep on light and the theme's cyan on dark.
 */
.btn-outline-primary {
  --bs-btn-color: #01757a;
  --bs-btn-border-color: #01757a;
  --bs-btn-hover-bg: #01757a;
  --bs-btn-hover-border-color: #01757a;
  --bs-btn-active-bg: #01757a;
  --bs-btn-active-border-color: #01757a;
  --bs-btn-disabled-color: #01757a;
  --bs-btn-disabled-border-color: #01757a;
  --bs-btn-focus-shadow-rgb: 1, 117, 122;
}

/*
 * The sidebar's current page is the other solid fill carrying a white label,
 * and the only one besides .btn-primary that this application actually renders
 * -- the three .bg-primary uses left are bare progress bars with no text in
 * them. The theme fills it from var(--bs-primary), so in the dark theme, where
 * that variable is deliberately still the bright cyan for the sake of ink, the
 * white label measured 2.24:1. Given the fill colour instead, in both themes,
 * for the same reason .btn-primary is.
 *
 * Specificity has to clear the theme's own (0,4,1) selector, which is what the
 * leading attribute selector is for.
 */
[data-bs-theme] .sidebar-nav ul .sidebar-item.selected > .sidebar-link,
[data-bs-theme] .sidebar-nav ul .sidebar-item.selected > .sidebar-link.active,
[data-bs-theme] .sidebar-nav ul .sidebar-item > .sidebar-link.active {
  background-color: #01757a;
  color: var(--bs-white);
}

[data-bs-theme="dark"] .btn-outline-primary {
  --bs-btn-color: #01c0c8;
  --bs-btn-border-color: #01c0c8;
  --bs-btn-hover-bg: #01c0c8;
  --bs-btn-hover-border-color: #01c0c8;
  --bs-btn-active-bg: #01c0c8;
  --bs-btn-active-border-color: #01c0c8;
  --bs-btn-disabled-color: #01c0c8;
  --bs-btn-disabled-border-color: #01c0c8;
  --bs-btn-focus-shadow-rgb: 1, 192, 200;
}

/* ------------------------------------------------------------------ *
 * Brand
 *
 * The Umbrella identity: an arch cut into four, whose four colours are the
 * four colours of the suite. templates/brand.php draws the mark; everything
 * below sizes and colours the lockup around it, and exposes the palette
 * as variables so the rest of the interface can use the same four
 * colours without copying hex values into a template.
 * ------------------------------------------------------------------ */

:root {
  --umbrella-violet: #7a5af8; /* Umbrella Cloud    */
  --umbrella-cyan: #01c0c8;   /* Umbrella LOG      */
  --umbrella-amber: #f59e0b;  /* Umbrella Business */
  --umbrella-coral: #f66d72;  /* family accent     */

  /* The mark read left to right. Used as a hairline wherever the
     interface needs to say "Umbrella" without repeating the logo. */
  --umbrella-spectrum: linear-gradient(90deg,
      var(--umbrella-violet) 0%,
      var(--umbrella-cyan) 34%,
      var(--umbrella-amber) 67%,
      var(--umbrella-coral) 100%);
}

/*
 * The lockup is HTML rather than a single SVG so the wordmark is set in the
 * page's own Plus Jakarta Sans and inherits the theme's ink colour -- see the
 * header of templates/brand.php. Sizes derive from --ubrand-size, the mark's
 * height in px, which the partial sets inline; one number scales the whole
 * lockup.
 */
.ubrand {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--bs-heading-color);
  text-decoration: none;
}

.ubrand-mark {
  flex: 0 0 auto;
  display: block;
}

.ubrand-text {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  line-height: 1.05;
}

.ubrand-name {
  font-size: calc(var(--ubrand-size, 34px) * 0.46);
  font-weight: 600;
  letter-spacing: -0.015em;
  white-space: nowrap;
}

.ubrand-product {
  font-weight: 800;
  margin-left: 0.3em;
}

/*
 * The endorsement line is tracked-out uppercase, which stops being legible
 * below about 8px however large the mark is -- hence the floor rather than a
 * plain ratio.
 */
.ubrand-sub {
  margin-top: 0.4em;
  font-size: max(8px, calc(var(--ubrand-size, 34px) * 0.185));
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bs-body-color);
  opacity: 0.7;
  white-space: nowrap;
}

/*
 * Under mini-sidebar the theme clips .logo-img to 40px and relies on overflow
 * to hide everything past the icon (`[data-sidebartype=mini-sidebar]
 * .logo-img{width:40px;overflow:hidden}`). That works only because the mark is
 * the leftmost thing in the lockup and nothing in it wraps.
 */
.left-sidebar .brand-logo .ubrand {
  gap: 0.5rem;
}

/*
 * A two-pixel spectrum under the sidebar's brand area. It is the cheapest way to
 * carry the four colours into the interface itself, and it doubles as the rule
 * that separates the brand from the navigation.
 */
.left-sidebar .brand-logo {
  position: relative;
}

.left-sidebar .brand-logo::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--umbrella-spectrum);
  opacity: 0.85;
}

/* -------------------------------------------------------------------
 * PingTrace
 *
 * The graph is markup, so its colours are the page's colours: custom
 * properties here rather than literals inside the SVG, which is what lets the
 * dark theme retune the whole thing in one block and lets a printed page fall
 * back sensibly.
 * ------------------------------------------------------------------- */

:root {
  /* How much of a sweep went missing, coldest to worst. The first step is
     exactly zero loss -- the difference between a link that drops nothing and
     one that drops a packet in twenty is the reason to look at this page at
     all, and one green for both would hide the start of every fault.

     Zero loss is the site's own success green rather than a literal, so the
     healthy line on this graph is the same colour as every healthy badge
     elsewhere -- and it retunes with the theme in one place. */
  --pt-loss-0: var(--bs-success);
  --pt-loss-1: #ffae1f;
  --pt-loss-2: #fa896b;
  --pt-loss-3: #fa5252;
  --pt-loss-4: #c92a2a;
  --pt-loss-5: #862e9c;

  /* The cloud. One ink at three opacities rather than three inks: the bands
     are nested, so the darkening in the middle is the layers adding up, and
     that only reads as one object if they are the same colour. */
  --pt-smoke: #5a6a85;
  --pt-grid: rgba(90, 106, 133, 0.18);
  --pt-frame: rgba(90, 106, 133, 0.35);
  --pt-axis-ink: #7c8fac;
  /* A stretch where nothing answered. Deliberately not one of the loss
     colours: it is the absence of a reading, not a reading. */
  --pt-down: rgba(250, 82, 82, 0.16);
}

[data-bs-theme="dark"] {
  --pt-loss-0: var(--bs-success);
  --pt-loss-1: #ffae1f;
  --pt-loss-2: #ff9e80;
  --pt-loss-3: #ff6b6b;
  --pt-loss-4: #ff3b3b;
  --pt-loss-5: #c77dff;

  --pt-smoke: #9fb3d1;
  --pt-grid: rgba(159, 179, 209, 0.16);
  --pt-frame: rgba(159, 179, 209, 0.3);
  --pt-axis-ink: #8ea1bd;
  --pt-down: rgba(255, 107, 107, 0.2);
}

.pt-smoke {
  display: block;
  width: 100%;
  overflow: visible;
}

/* The bands are drawn as polygons whose vertical extent is the data. Scaling
   the SVG horizontally to the card would scale the stroke widths with it, so
   preserveAspectRatio is off and everything is sized in user units. */
.pt-band {
  fill: var(--pt-smoke);
  stroke: none;
}

.pt-band-0 { fill-opacity: 0.14; }
.pt-band-1 { fill-opacity: 0.20; }
.pt-band-2 { fill-opacity: 0.28; }

/* A run of one slot has no width to fill and is drawn as a stroke instead. */
line.pt-band {
  fill: none;
  stroke: var(--pt-smoke);
  stroke-opacity: 0.45;
  stroke-width: 1.2;
}

.pt-median {
  fill: none;
  /* 1.6 rather than the 1.4 it was: a deeper colour only reads as deeper if
     there is enough of it on screen to read. */
  stroke-width: 1.6;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.pt-down {
  fill: var(--pt-down);
}

.pt-grid {
  stroke: var(--pt-grid);
  stroke-width: 1;
}

.pt-frame {
  fill: none;
  stroke: var(--pt-frame);
  stroke-width: 1;
}

.pt-ylab,
.pt-xlab,
.pt-empty {
  fill: var(--pt-axis-ink);
  font-size: 9px;
  font-family: inherit;
}

.pt-ylab { text-anchor: end; }
.pt-xlab { text-anchor: middle; }
.pt-empty { font-size: 11px; }

.pt-smoke-empty {
  border: 1px dashed var(--pt-grid);
  border-radius: 0.3rem;
}

/*
 * The graph across the bottom of a host card.
 *
 * It sits outside .card-body precisely so the body's padding does not apply:
 * the graph runs to the card's left and right edges and sits on its bottom
 * one. `overflow:hidden` plus the inherited radius is what stops the corners
 * of a square SVG poking out through the card's rounded ones -- a detail that
 * is invisible until the card has a border, and then looks like a rendering
 * fault.
 *
 * The minimum height keeps a card whose readings have not arrived the same
 * size as its neighbours; a grid that reflows as data lands reads as a page
 * still loading.
 */
.pt-thumb {
  display: block;
  margin-top: 0.75rem;
  min-height: 76px;
  overflow: hidden;
  border-bottom-left-radius: var(--bs-card-border-radius, 0.5rem);
  border-bottom-right-radius: var(--bs-card-border-radius, 0.5rem);
}

.pt-thumb svg {
  display: block;
}

/*
 * The round trip, at the size the card is read at. Tabular figures because a
 * grid of these is scanned down, and proportional digits make the decimal
 * points wander.
 */
.pt-metric {
  font-size: 1.75rem;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--bs-heading-color);
}

.pt-metric-unit {
  font-size: 0.8rem;
  font-weight: 500;
  margin-left: 0.1rem;
  color: var(--bs-secondary-color);
}

.pt-trend {
  display: inline-flex;
  align-items: center;
  gap: 0.1rem;
  margin-left: 0.35rem;
  font-size: 0.75rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/*
 * The verdict badge: Stable, Healthy, Unstable, Broken.
 *
 * This badge is where the theme's pastel red and amber were first found to be
 * unreadable as 12px words, and it carried its own deepened ink for a while.
 * That deepening is now the application's own --bs-danger and --bs-warning in
 * the light theme -- the block above -- so the light rules that used to live
 * here have gone: they would set the colour the badge already inherits.
 *
 * The dark rules stay. The pastels are legible on a dark ground, so they are
 * left alone site-wide, but on this badge the word is the whole signal and
 * these two lift it a little further off the tint it sits on.
 */
[data-bs-theme="dark"] .pt-verdict.text-danger {
  color: #ff9e80 !important;
}

[data-bs-theme="dark"] .pt-verdict.text-warning {
  color: #ffc44d !important;
}

/*
 * A genuinely neutral tone, for "Not watched" and "No reading yet".
 *
 * Bootstrap's `secondary` is the obvious choice for these and is the wrong one
 * here: under Cyan_Theme, Modernize redefines `--bs-secondary` to #fb9678, a
 * coral. So `.bg-secondary-subtle .text-secondary` -- which reads as "grey,
 * nothing to see" everywhere else in the world -- comes out the colour of an
 * alarm, and a host somebody deliberately stopped watching looks like a host
 * that is on fire. This pair is tied to the body ink instead, so it is grey in
 * both themes and stays grey whatever colour theme is selected.
 */
.bg-neutral-subtle {
  background-color: rgba(var(--bs-body-color-rgb), 0.09) !important;
}

.text-neutral {
  color: var(--bs-secondary-color) !important;
}

/* The menu in the top right corner of a host card. Same treatment as the
   folder card's, so the two read as one control in two places. */
.pt-host-menu {
  background: none;
  border: 0;
  padding: 0.15rem 0.2rem;
  line-height: 1;
  border-radius: 0.25rem;
  color: var(--bs-secondary-color);
  opacity: 0.5;
}

.pt-host:hover .pt-host-menu,
.pt-host-menu:hover,
.pt-host-menu:focus-visible {
  opacity: 1;
  color: var(--bs-primary);
}

/* ---- the folder cards -------------------------------------------------
 *
 * A folder is opened by clicking anywhere on its card, but the control that
 * does it is a real <button> around the name rather than a handler on a div,
 * so it is reachable from a keyboard. The button carries none of a button's
 * appearance: the card is the thing being drawn.
 */
.pt-group-btn {
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
  text-align: left;
}

.pt-group {
  cursor: pointer;
  border: 1px solid transparent;
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

.pt-group:hover {
  border-color: var(--bs-border-color);
}

/* The open folder is outlined rather than filled: the card is already tinted
   by the health of what is in it, and a second background would be a second
   thing to read a colour off. */
.pt-group-open,
.pt-group-open:hover {
  border-color: var(--bs-primary);
  box-shadow: 0 0 0 1px var(--bs-primary);
}

.pt-caret {
  transition: transform 0.15s ease-in-out;
}

.pt-group-open .pt-caret {
  transform: rotate(180deg);
}

/* Rename and remove. Dimmed rather than hidden until hover -- a control that
   appears only on hover is a control a touchscreen cannot find. */
.pt-group-edit {
  background: none;
  border: 0;
  padding: 0.15rem 0.2rem;
  line-height: 1;
  border-radius: 0.25rem;
  color: var(--bs-secondary-color);
  opacity: 0.5;
}

.pt-group:hover .pt-group-edit,
.pt-group-edit:hover,
.pt-group-edit:focus-visible {
  opacity: 1;
  color: var(--bs-primary);
}

/* ---- the host cards --------------------------------------------------- */

.pt-host {
  transition: box-shadow 0.15s ease-in-out;
}

.pt-host:hover {
  box-shadow: var(--bs-box-shadow-sm, 0 2px 6px rgba(0, 0, 0, 0.08));
}

/*
 * The card's whole area is a link to the host's own page (Bootstrap's
 * .stretched-link, whose ::after sits at z-index 1 over everything in the
 * card). Anything that has to stay clickable in its own right has to be
 * lifted above it -- without this the Edit button is covered by the link and
 * opens the graphs instead, which looks like a broken button rather than a
 * stacking problem.
 */
.pt-actions {
  position: relative;
  z-index: 2;
}

/* Flex and grid children default to min-width:auto, which stops text-truncate
   working: the text sets the floor and the box grows instead of ellipsing. */
.pt-min-0 {
  min-width: 0;
}

/* ---- the graph cards -------------------------------------------------- */

.pt-graph {
  cursor: pointer;
  transition: box-shadow 0.15s ease-in-out;
}

.pt-graph:hover {
  box-shadow: var(--bs-box-shadow-sm, 0 2px 6px rgba(0, 0, 0, 0.08));
}

/* The dialog's plot area, kept at the height of a drawn graph so that
   replacing one range with another does not collapse the modal and bounce it
   back to centre while the reader is looking at it. */
#ptGraphPlot {
  min-height: 300px;
}

/* The legend sits in a flex strip above the grid, which owns the spacing --
   see pingtrace.php. Its own bottom margin would push it off that line's
   baseline, so it has none; on the host page, where it sits above a graph, the
   graph's own padding does the work. */
.pt-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.75rem;
  color: var(--pt-axis-ink);
}

.pt-legend-title {
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
}

.pt-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}

.pt-legend-item i {
  width: 0.85rem;
  height: 0.3rem;
  border-radius: 0.15rem;
  display: inline-block;
}

.pt-legend-down {
  background: var(--pt-down);
  outline: 1px solid var(--pt-frame);
}

/* The crosshair and the readout, both added by assets/js/pingtrace.js. The
   graph is complete without either: this is for reading a value off it. */
.pt-cursor {
  stroke: var(--pt-frame);
  stroke-width: 1;
  stroke-dasharray: 2 2;
  pointer-events: none;
}

.pt-tip {
  position: fixed;
  z-index: 1080;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.4rem 0.6rem;
  border-radius: 0.35rem;
  font-size: 0.75rem;
  line-height: 1.35;
  font-variant-numeric: tabular-nums;
  color: var(--bs-body-color);
  background: var(--bs-body-bg);
  border: 1px solid var(--pt-frame);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.16);
  white-space: nowrap;
}

.pt-tip[hidden] {
  display: none;
}

.pt-tip b {
  font-weight: 600;
}

.pt-tip-ms {
  font-size: 0.95rem;
}

.pt-tip-none,
.pt-tip-loss {
  font-size: 0.7rem;
}

.pt-tip-none { color: var(--pt-axis-ink); }
.pt-tip-loss-0 { color: var(--pt-loss-0); }
.pt-tip-loss-1 { color: var(--pt-loss-1); }
.pt-tip-loss-2 { color: var(--pt-loss-2); }
.pt-tip-loss-3 { color: var(--pt-loss-3); }
.pt-tip-loss-4 { color: var(--pt-loss-4); }
.pt-tip-loss-5 { color: var(--pt-loss-5); }

/* ---- which way out ----------------------------------------------------
 *
 * The interface a watched address leaves the network by, under its address on
 * the card. Drawn in the primary ink rather than muted: it is the answer to
 * "which path is this on", which is a thing being looked for, and a third grey
 * line under two grey lines is a thing that is scrolled past.
 */
.pt-path {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  color: var(--bs-primary);
  opacity: 0.85;
}

.pt-path i {
  flex-shrink: 0;
  font-size: 0.85rem;
}

/* ---- the traceroute dialog --------------------------------------------- */

/* The result area keeps its height while a second trace runs, so the dialog
   does not collapse and bounce back to centre under whoever is reading it. */
#ptTraceResult {
  min-height: 120px;
}

.pt-trace td,
.pt-trace th {
  padding-top: 0.35rem;
  padding-bottom: 0.35rem;
}

.pt-trace-ms {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* A hop nobody answered is dimmed rather than hidden: the row is evidence
   about where the path stops being visible, and removing it would renumber
   every hop after it. */
.pt-trace-silent td {
  opacity: 0.6;
}

/* The round trip, drawn against the slowest hop in this trace. What an
   operator is looking for is the step -- the hop where the number jumps -- and
   a bar finds that faster than a column of figures does. */
.pt-trace-bar {
  display: block;
  height: 6px;
  min-width: 2px;
  border-radius: 3px;
  background: var(--bs-primary);
  opacity: 0.35;
}

/* ---- PingTrace's table view -------------------------------------------
 *
 * The other way round the same readings. Cards answer "which part of the
 * network is unhappy"; this answers "where is this one host", which is the
 * question that starts arriving once there are more hosts than fit on a
 * screen. Nothing here restyles the theme's table -- it is the theme's own
 * .table, with the few things a row of readings needs that a row of text
 * does not.
 */

/* A sortable heading has to look like something to press. The cursor and the
 * hover ink are the whole affordance; an arrow drawn on every heading would
 * be six arrows pointing nowhere until one of them is used. */
.pt-table th.pt-sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

.pt-table th.pt-sortable:hover,
.pt-table th.pt-sortable:focus-visible {
  color: var(--bs-primary);
}

/* The arrow appears only on the column actually in use, and points the way
 * the rows are ordered. ▲ is ascending, which for Status means best first --
 * the table opens the other way round, worst first, and says so. */
.pt-table th.pt-sortable[aria-sort]::after {
  content: '\25BC';
  font-size: 0.6em;
  margin-left: 0.35rem;
  opacity: 0.6;
}

.pt-table th.pt-sortable[aria-sort="ascending"]::after {
  content: '\25B2';
}

/* Two lines of identity in one cell -- the name over the address -- want a
 * ceiling, or one long note stretches the column past everything else and
 * pushes the readings off the right of the table. */
.pt-cell-wide {
  max-width: 22rem;
}

/* The thumbnail column takes what is left. Fixed rather than fluid so that
 * filtering the table down to three rows does not redraw every graph at a
 * different width from the one above it. */
.pt-table .pt-col-graph {
  width: 240px;
}

.pt-row-thumb {
  width: 240px;
  height: 40px;
  overflow: hidden;
  border-radius: 0.25rem;
}

.pt-row-thumb svg {
  display: block;
}

/* The row's menu is the card's menu, but a table row has no hover target of
 * its own to reveal it from, so it sits at the card's resting opacity and
 * lifts on the row rather than on the card. */
.pt-table tbody tr:hover .pt-host-menu {
  opacity: 1;
  color: var(--bs-primary);
}

/* The search box: the theme has no input-with-an-icon, and an input-group
 * would put a bordered box around the icon. */
.pt-search {
  position: relative;
}

.pt-search i {
  position: absolute;
  left: 0.6rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--bs-secondary-color);
  pointer-events: none;
  font-size: 1rem;
}

.pt-search input {
  padding-left: 2rem;
  min-width: 14rem;
}

/*
 * The footer, pinned bottom-right.
 *
 * `mt-auto` needs two things the theme does not give it: a flex column to sit
 * in, and a column taller than its contents. Modernize's .body-wrapper is a
 * plain block with a 70px top padding and no height at all, so on a short page
 * (Licence, Settings, an empty search) the credit simply followed the last card
 * and floated in the middle of the screen.
 *
 * The height is claimed on the container rather than the wrapper because the
 * 70px is the wrapper's own padding -- taking `100vh` there and `border-box`
 * together would be right, but the horizontal layout overrides that padding in
 * three separate rules and this one has to hold under all of them.
 *
 * Not position:fixed. That version laid the credit over the last row of every
 * long table, which on this product means over evidence.
 */
.body-wrapper > .container-fluid {
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - 70px);
}

/* A hairline rather than a border: the page above it is already a stack of
 * bordered cards, and a full-weight rule reads as one more of them. */
.umb-footer {
  border-top: 1px solid var(--bs-border-color);
  opacity: 0.85;
}
