:root {
  --console-bg: #0c0c0c;
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.phillux-layout {
  flex: 1;
  display: flex;
  align-items: stretch;
  width: 100%;
}

.phillux-sidenav {
  flex: 0 0 220px;
  max-width: 220px;
  box-sizing: border-box;
  border-right: 1px solid var(--clr-global-borderColor, #e8e8e8);
  padding: 1rem 0;
}

.phillux-sidenav .nav {
  display: flex;
  flex-direction: column;
  align-items: stretch;
}

.phillux-sidenav .nav-item {
  width: 100%;
}

.phillux-sidenav .nav-link {
  display: flex;
  align-items: center;
  width: 100%;
  box-sizing: border-box;
}

.phillux-sidenav .nav-link.active {
  font-weight: 600;
  color: var(--clr-color-action-700, #0072a3);
  background: rgba(0, 114, 163, .08);
}

.phillux-sidenav clr-icon {
  margin-right: .4rem;
}

.phillux-content {
  flex: 1 1 auto;
  width: 100%;
  padding: 1.2rem 1.6rem 3rem;
  overflow-x: auto;
  min-width: 0;
  box-sizing: border-box;
}

.branding .title {
  font-weight: 600;
  margin-left: .3rem;
}

/* Der Phillux-Schriftzug in der Kopfzeile (branding/mk-web-logo.py).
 *
 * Als Maske und nicht als <img>: in einem <img> ist das SVG ein eigenes
 * document, where 'currentColor' inherits nothing and falls back to black —
 * on the dark header the wordmark would be invisible. Going through the
 * Maske kommt die Farbe aus dem CSS und stimmt auf jedem Untergrund.
 *
 * Height plus aspect ratio instead of a fixed width: the grid is 47x7, the
 * Breite ergibt sich daraus. */
.branding .wordmark {
  display: block;
  height: 17px;
  aspect-ratio: 47 / 7;
  /* White on purpose and not currentColor: Clarity gives the links in the
     header a dimmed white, and the wordmark is meant to be at full
     durchzeichnen — er ist die Marke, kein Navigationspunkt. */
  background-color: #fff;
  -webkit-mask: url("logo.svg") no-repeat center / contain;
  mask: url("logo.svg") no-repeat center / contain;
}

.conn-indicator {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-right: .6rem;
  background: #999;
  /* Die Kopfzeile ist ein Flex-Container; ohne diese Zeile richtet sich der
     Punkt an der Grundlinie der Nachbarn aus und sitzt sichtbar zu hoch. */
  align-self: center;
}
.conn-ok { background: #4caf50; }
.conn-bad { background: #d0021b; }

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
  flex-wrap: wrap;
  gap: .5rem;
}

.page-header h2 {
  margin: 0;
}

.toolbar {
  display: flex;
  gap: .5rem;
  align-items: center;
  flex-wrap: wrap;
}

.toast-stack {
  position: fixed;
  top: 4.2rem;
  right: 1rem;
  z-index: 3000;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  max-width: 380px;
}

.toast-stack .alert {
  box-shadow: 0 2px 8px rgba(0,0,0,.15);
}

.badge-state {
  text-transform: capitalize;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.stat-card {
  border: 1px solid var(--clr-global-borderColor, #e8e8e8);
  border-radius: .2rem;
  padding: 1rem 1.2rem;
  background: var(--clr-global-app-background, #fff);
}

.stat-card .stat-label {
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: #767676;
}

.stat-card .stat-value {
  font-size: 1.6rem;
  font-weight: 600;
  margin-top: .2rem;
}

.stat-card .stat-sub {
  font-size: .78rem;
  color: #767676;
}

.meter {
  display: flex;
  flex-direction: column;
  gap: .35rem;
}

.meter-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: .82rem;
}

.meter-label {
  color: #52514e;
}

.meter-value {
  font-weight: 600;
  color: #0b0b0b;
}

.meter-track {
  height: 8px;
  border-radius: 999px;
  overflow: hidden;
}

.meter-fill {
  height: 100%;
  border-radius: 999px;
  transition: width .4s ease, background-color .3s ease;
}

.meter-sub {
  font-size: .74rem;
  color: #898781;
}

.storage-usage-col {
  min-width: 200px;
}

.meter-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1.2rem 1.6rem;
}

.detail-tabs {
  margin-top: 1rem;
}

.console-wrap {
  background: var(--console-bg);
  border-radius: .3rem;
  padding: .75rem;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: .6rem;
}

.console-screen {
  width: 100%;
  height: 70vh;
  background: #000;
  border: 1px solid #333;
  overflow: hidden;
}

.console-screen canvas {
  outline: none;
}

.console-keys {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem;
}

.form-hint {
  font-size: .78rem;
  color: #767676;
  margin-top: .2rem;
}

.section-divider {
  border: none;
  border-top: 1px solid var(--clr-global-borderColor, #e8e8e8);
  margin: 1rem 0;
}

/* The hardware part of the creation dialogue and of the detail view. Folded
   away by default: it is a page of settings that nearly every VM is right
   without, and unfolded it would push the two fields everybody does fill in
   (name and size) out of sight. */
.hw-details {
  border: 1px solid var(--clr-global-borderColor, #e8e8e8);
  border-radius: 3px;
  padding: .4rem .7rem;
  margin: .6rem 0;
}
.hw-details > summary {
  cursor: pointer;
  font-weight: 500;
}
.hw-details[open] > summary { margin-bottom: .5rem; }
.hw-group + .hw-group {
  border-top: 1px solid var(--clr-global-borderColor, #e8e8e8);
  margin-top: .7rem;
  padding-top: .5rem;
}
.hint-warn { color: var(--clr-color-danger-700, #c92100); }
.hw-group-title {
  margin: 0 0 .3rem;
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: #565656;
}

.empty-state {
  text-align: center;
  padding: 3rem 1rem;
  color: #767676;
}

.mono {
  font-family: "Courier New", monospace;
  font-size: .82rem;
}

.progress-inline {
  display: flex;
  align-items: center;
  gap: .5rem;
}

.clickable-row {
  cursor: pointer;
}

.tag-row {
  display: flex;
  gap: .4rem;
  flex-wrap: wrap;
}

.login-screen {
  max-width: 460px;
  margin: 4rem auto;
}

.spin {
  animation: phillux-spin 1s linear infinite;
  display: inline-block;
}
@keyframes phillux-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

/* Zertifikate-Tab */
.cert-label {
  font-weight: 600;
  width: 220px;
  white-space: nowrap;
  vertical-align: top;
}

/* Sign-in: the way back over an API key, collapsed by default */
.login-fallback {
  max-width: 460px;
  margin: 1rem auto 0;
}
.login-fallback > summary {
  cursor: pointer;
  color: #0072a3;
  font-size: .55rem;
}
.login-fallback > * + * {
  margin-top: .5rem;
}

.back-link {
  font-weight: 400;
}

/* While a dialog stands, the page behind it holds still: no scrolling past it
   (the wheel over the backdrop used to take the page along), and the pollers
   leave the view alone — see modalOpen() in ui.js. The padding takes the place
   of the scrollbar that goes away, otherwise everything jumps to the right by
   its width the moment the dialog opens. */
body.modal-open {
  overflow: hidden;
}

/* Tall dialogs (creating a VM with cloud-init and a fixed IP) grew past the
   edge of the screen — the "Cancel"/"Create" buttons were out of reach and the
   dialog could neither be submitted nor closed. So the body gets a scrollbar
   of its own while header and footer stay put. */
.modal .modal-dialog,
.modal .modal-content {
  max-height: calc(100vh - 3rem);
}
.modal .modal-content {
  display: flex;
  flex-direction: column;
}
.modal .modal-body {
  overflow-y: auto;
  /* Ohne das schrumpft der Rumpf in einem Flex-Container nicht unter seine
     content height and the limit above would come to nothing. */
  min-height: 0;
  /* Sobald die senkrechte Bildlaufleiste auftaucht, wird der Rumpf schmaler.
     Clarity builds .clr-row with negative margins — those stick out past the
     edge and add a horizontal bar as well, even though
     inhaltlich nichts zu scrollen ist. */
  overflow-x: hidden;
}
/* Instead of clipping the negative margins, they are not applied inside the
   erst gesetzt: die Spalten bringen ihren eigenen Abstand mit. */
.modal .modal-body .clr-row {
  margin-left: 0;
  margin-right: 0;
}
.modal .modal-header,
.modal .modal-footer {
  flex: 0 0 auto;
}
/* A dialogue with many buttons — the brake's incident has six — squeezed them
   all into one line and cut every label off after four letters ("LET THE…",
   "SEA…", "DELE…"). A button whose text is gone is a button nobody dares press.
   So: the row wraps, and no button is allowed to shrink below its own words. */
.modal .modal-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: .4rem;
  padding: .7rem 1.1rem .9rem;
  /* Clarity nails the footer to 4.2rem in three properties at once. Wrapping
     into a second line would be cut off by that, so all three go. */
  height: auto;
  min-height: 0;
  max-height: none;
}
.modal .modal-footer .btn {
  flex: 0 0 auto;
  min-width: 0;
  max-width: 100%;
  margin: 0;
  white-space: nowrap;
  overflow: visible;
  text-overflow: clip;
}

/* Dialogformulare: Clarity legt Label und Feld sonst ohne Abstand nebeneinander,
   which sticks together with longer labels. Inside the modal the label
   therefore sits above the field. */
.modal .form-group {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  margin-bottom: .75rem;
}
/* The label used to be smaller than the hint underneath the field — which read
   as if the explanation mattered more than what is being asked for. Now the
   order is: label, field, hint, each a step quieter than the one before. */
.modal .form-group > label {
  margin: 0 0 .25rem;
  font-size: .68rem;
  font-weight: 600;
  color: #313131;
  line-height: 1.3;
}
.modal .form-hint {
  font-size: .62rem;
  line-height: 1.45;
  color: #6b6b6b;
  margin-top: .25rem;
}
/* A checkbox with its text beside it is not a label above a field — there the
   flex column above would break the line between box and text. */
.modal .form-group > label > input[type="checkbox"] {
  margin-right: .35rem;
}
.modal .modal-body {
  padding: 1rem 1.1rem 1.2rem;
}
.modal .section-divider { margin: 1.1rem 0 .9rem; }

/* ---- Dialogs with tabs -------------------------------------------------- */

.modal-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: .15rem;
  border-bottom: 1px solid var(--clr-global-borderColor, #e8e8e8);
  margin: 0 0 1rem;
}
.modal-tab {
  appearance: none;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: .45rem .8rem;
  font: inherit;
  font-size: .68rem;
  font-weight: 500;
  color: #565656;
  cursor: pointer;
}
.modal-tab:hover { color: #0072a3; }
.modal-tab.active {
  color: #0072a3;
  border-bottom-color: #0072a3;
}
.modal-pane-hint {
  margin: -.3rem 0 .8rem;
}
.modal .form-group > .clr-input,
.modal .form-group > .clr-select,
.modal .form-group > select {
  width: 100%;
}

/* Rechte-Editor */
.perm-group {
  margin-bottom: 1rem;
}
.perm-group-title {
  margin: 0 0 .3rem;
  font-size: .6rem;
  font-weight: 600;
  color: #454545;
  border-bottom: 1px solid #e8e8e8;
  padding-bottom: .2rem;
}
.perm-key {
  margin-left: .4rem;
  font-size: .5rem;
  color: #8c8c8c;
  background: #f5f5f5;
  padding: 0 .25rem;
  border-radius: .15rem;
}
.perm-presets {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .3rem;
  margin-bottom: 1rem;
  padding-bottom: .75rem;
  border-bottom: 1px solid #e8e8e8;
}
.perm-presets .form-hint {
  margin: 0 .2rem 0 0;
}

/* Secondary text in table cells. Do not use .form-hint: that is meant for
   forms and at .78rem larger than Clarity's table font — in a cell the
   secondary information would end up bigger than the main information.
   em statt rem, damit es sich an die jeweilige Zelle anpasst. */
.cell-sub {
  font-size: .85em;
  color: #767676;
}
div.cell-sub {
  display: block;
  line-height: 1.35;
}

/* Firewall-Regeleditor */
.fw-table th,
.fw-table td {
  padding: .2rem .3rem;
  vertical-align: middle;
}
.fw-table .clr-select,
.fw-table .clr-input {
  width: 100%;
  min-width: 0;
}
.fw-ports {
  display: flex;
  align-items: center;
  gap: .2rem;
}
.fw-ports .fw-port {
  width: 4.2rem;
}
.fw-ruleset {
  background: #0c0c0c;
  color: #ddd;
  padding: .75rem;
  border-radius: .3rem;
  max-height: 60vh;
  overflow: auto;
  white-space: pre;
  font-size: .55rem;
  line-height: 1.4;
}

/* Storage: Inhaltstyp mit Standard-Schalter */
.type-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  padding: .2rem 0;
  border-bottom: 1px solid #f0f0f0;
}
.type-row .clr-checkbox-wrapper {
  margin: 0;
}

/* Running background operations in the header */
.task-indicator {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: 0 .6rem;
  color: #fff;
  text-decoration: none;
  font-size: .55rem;
  white-space: nowrap;
}
.task-indicator:hover {
  background: rgba(255, 255, 255, .15);
}

/* Doku-Symbol rechts in der Kopfzeile.
 *
 * Clarity dims the icons with 'opacity: .65' and takes the dimming back on
 * hover — with this rule, however:
 *
 *     .header .header-actions .nav-link:enabled:hover { opacity: 1 }
 *
 * ':enabled' trifft nur Formularelemente. Der Doku-Knopf ist ein <a> und
 * damit nie ':enabled', die Regel greift bei ihm also nie. Beim Konto daneben
 * funktioniert es, weil das ein <button> ist. Hier dieselbe Aufhellung ohne
 * diese Bedingung. */
.header .header-actions a.nav-link:hover,
.header .header-actions a.nav-link:focus-visible {
  opacity: 1;
}
/* Clarity colours spinners dark — in the header it has to be light. */
.task-indicator .spinner {
  width: .7rem;
  height: .7rem;
  min-width: .7rem;
  min-height: .7rem;
  border-color: rgba(255, 255, 255, .35);
  border-top-color: #fff;
}

/* Spinner inside a button: Clarity would otherwise draw it too big and dark. */
.btn .spinner.spinner-inline {
  width: .7rem;
  height: .7rem;
  min-width: .7rem;
  min-height: .7rem;
  margin: 0;
  vertical-align: middle;
}
.btn-primary .spinner.spinner-inline,
.btn-danger .spinner.spinner-inline {
  border-color: rgba(255, 255, 255, .4);
  border-top-color: #fff;
}

/* Container console: xterm needs an inner area with some padding,
   sonst klebt die erste Spalte am Rahmen. */
.console-term {
  padding: .4rem;
}
.console-term .xterm-viewport {
  overflow-y: auto;
}

/* Licence lock: navigation entries stay visible but are no longer operable.
   Hiding it would be worse — nobody would know what is missing and why. The
   cursor says "not allowed" explicitly. */
.nav-item.nav-locked {
  opacity: .4;
  pointer-events: none;
  cursor: not-allowed;
}

/* Notice bar above the page content. It has to stand out without covering
   anything up — hence in the flow and not floating. */
.licence-bar {
  padding: .55rem .9rem;
  margin-bottom: 1rem;
  border-radius: .15rem;
  font-size: .62rem;
  line-height: 1.5;
}
.licence-bar.yellow { background: #fdf3d7; border: 1px solid #efc16a; color: #5a4300; }
.licence-bar.red  { background: #f8dcd8; border: 1px solid #d98a80; color: #7a1c11; }
.licence-bar a { margin-left: .4rem; font-weight: 600; }

/* An empty required field. Red alone would be nothing to the colour blind —
   hence the heavier outline as well. */
.field-missing,
input.field-missing,
select.field-missing {
  border-bottom: 2px solid #c92100 !important;
  background: rgba(201, 33, 0, .05);
}

/* Boot order. Deselected devices stay visible, only toned down — whoever does
   not see that they exist looks for the setting somewhere else. */
.boot-list { list-style: none; margin: 0 0 .8rem; padding: 0; }
.boot-list li {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .25rem .4rem;
  border-bottom: 1px solid #e8e8e8;
}
.boot-list li:last-child { border-bottom: 0; }
.boot-list li.boot-off { opacity: .55; }
.boot-no {
  min-width: 1.2rem;
  font-size: .58rem;
  color: #565656;
  text-align: center;
}
.boot-list label { margin: 0; flex: 1; font-size: .62rem; }
.boot-arrows { display: flex; gap: .1rem; }
.boot-arrows .btn { margin: 0; min-width: auto; padding: 0 .3rem; }

/* Scope of the licence. What is locked is shown instead of hidden — whoever
   does not see what else there is asks the support about it. */
.feature-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: .35rem 1rem;
}
.feature-entry { display: flex; align-items: center; gap: .4rem; font-size: .62rem; }
.feature-entry.free { color: #266900; }
.feature-entry.locked { color: #737373; }
.feature-entry.locked span { text-decoration: line-through; }
.feature-mark { width: .7rem; height: .7rem; }

/* ============================================================
   Responsive layout
   ------------------------------------------------------------
   Wide screens keep the fixed 220px sidebar. On narrow ones it
   turns into an off-canvas drawer, opened by the hamburger in the
   header. The content grids already flow (auto-fit), so what is
   left is: wide tables scroll in their own box, dialogs and toasts
   go full width, and a few paddings shrink.
   ============================================================ */

/* A wide table scrolls inside this box, not the page. Momentum scrolling on
   touch, and no vertical clipping (a dropdown in a cell must not be cut off). */
/* Labs: one card per instance and per description in the catalogue. */
.lab-card .card-block {
  display: flex;
  flex-direction: column;
}
/* The instance card: what the lab is made of, its stages, and the picture, side
   by side. The selector carries two classes on purpose — .lab-card .card-block
   above sets a column direction, and a single class would lose against it and
   stack everything below one another. On narrow screens that stacking is what
   we want, and the media queries say so. */
.lab-card .lab-body {
  display: grid;
  grid-template-columns: minmax(190px, 250px) minmax(200px, 260px) minmax(0, 1fr);
  gap: 1rem;
  align-items: stretch;
}
@media (max-width: 1200px) {
  .lab-card .lab-body { grid-template-columns: minmax(220px, 300px) minmax(0, 1fr); }
  /* The picture goes below the two lists and takes the whole width. */
  .lab-topology { grid-column: 1 / -1; }
}
/* The line between the guests and their stages belongs to the three-column
   layout only — stacked, a line on the left would divide nothing. */
@media (min-width: 1201px) {
  .lab-card .lab-stage-block {
    border-left: 1px solid var(--clr-global-borderColor, #e8e8e8);
    padding-left: .8rem;
  }
}
@media (max-width: 900px) {
  .lab-card .lab-body { grid-template-columns: 1fr; }
}
.lab-column { min-width: 0; }
.lab-title { margin: 0 0 .4rem; }
.lab-topology {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
/* The picture sits in a box of its own: the drawing may be bigger than the box,
   and what sticks out is reached by dragging or zooming, not by growing the
   card. */
.lab-canvas {
  position: relative;
  /* A fixed height on purpose: with 'auto' the drawing pulls itself up to its
     own aspect ratio and a card of three guests becomes 700 px tall. It is a
     window onto the lab — what does not fit is reached by zoom and drag. */
  height: 300px;
  overflow: hidden;
  border: 1px solid var(--clr-global-borderColor, #e8e8e8);
  border-radius: .15rem;
  background: #fcfcfc;
  cursor: grab;
  touch-action: none;
}
/* The buttons of a lab, in a column of 250 px: a btn-group holds them in one
   row come what may, and Tear down then stood outside the card. Here they wrap
   instead, each one with its own frame — a group broken over two lines would
   carry its joined edges into the second. */
.lab-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  margin-top: .4rem;
}
.lab-actions .btn { margin: 0; }
.lab-canvas.is-big { height: 68vh; }
.lab-canvas canvas { outline: none; }
/* The buttons of the picture stand over it and carry their name — as bare
   icons in a corner of the canvas they were looked for and not found. */
.lab-tools {
  margin: 0 0 .3rem;
  align-self: flex-start;
}
/* vis-network brings a stylesheet of 216 KB along for its own navigation
   buttons, which we do not use. Only the tooltip needs a few lines, and
   without them it would drop into the page as plain text. */
.vis-tooltip {
  position: absolute;
  visibility: hidden;
  z-index: 5;
  padding: .2rem .4rem;
  white-space: nowrap;
  font-size: .7rem;
  color: #fff;
  background: #313131;
  border-radius: .15rem;
  pointer-events: none;
}
.lab-canvas-hint {
  margin: .25rem 0 0;
  font-size: .65rem;
  color: #767676;
}
.lab-stage-block { min-width: 0; }
.lab-stage-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
}
/* A stage stands in a narrow column: name, when and how many, then the buttons
   — one under the other. Side by side they tore the column apart. */
.lab-stage {
  padding: .3rem 0;
}
.lab-stage + .lab-stage { border-top: 1px dotted var(--clr-global-borderColor, #e8e8e8); }
.lab-stage-label {
  font-weight: 500;
  margin-left: .25rem;
}
.lab-stage .cell-sub { display: block; }
.lab-stage .lab-actions { margin-top: .1rem; }

.lab-stage-broken {
  color: var(--clr-color-danger-700, #c92100);
  margin: .15rem 0 .2rem;
}

/* Twenty participants in a dialogue: a column of ticks that stays inside the
   modal instead of pushing it off the screen. */
.lab-classroom-users {
  max-height: 15rem;
  overflow-y: auto;
  border: 1px solid var(--clr-global-borderColor, #e8e8e8);
  padding: .4rem .6rem;
  margin-bottom: .8rem;
  columns: 2;
}
.lab-classroom-user {
  display: block;
  break-inside: avoid;
  font-size: .65rem;
  padding: .15rem 0;
}

/* The setup of a guest is folded away: most guests need none, and open it would
   push the guest row apart for everybody. */
.lab-setup {
  flex: 1 1 100%;
  grid-column: 1 / -1;
  margin-top: .35rem;
  padding: .35rem .5rem;
  border: 1px dotted var(--clr-global-borderColor, #e8e8e8);
}
/* ---- The workbench of the participant ----------------------------------
   Two columns: the exercise wide on the left, the machines narrow on the
   right. Under 60rem they fall under each other — the task first, because
   that is what is read, and the machines after it. */
.lab-work {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(16rem, 1fr);
  gap: 1rem;
  align-items: start;
}
@media (max-width: 60rem) {
  .lab-work { grid-template-columns: 1fr; }
}
.lab-work-intro {
  font-size: .8rem;
  color: #454545;
  margin: 0 0 .8rem;
  max-width: 60rem;
}
.lab-work-marks {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: center;
  margin-top: .6rem;
}
.lab-step { margin-bottom: .5rem; }
.lab-step-head {
  display: flex;
  align-items: center;
  gap: .6rem;
  width: 100%;
  padding: .55rem .8rem;
  background: none;
  border: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.lab-step-head:hover { background: #f5f8fa; }
.lab-step-no {
  flex: 0 0 auto;
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 50%;
  background: #e8e8e8;
  color: #454545;
  font-size: .62rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.lab-step-title { flex: 1; font-weight: 500; font-size: .78rem; }
.lab-step-text { font-size: .8rem; line-height: 1.5; white-space: pre-wrap; }
.lab-check-list {
  margin: .2rem 0 .6rem 1rem;
  font-size: .68rem;
  color: #565656;
}
.lab-hint, .lab-solution {
  font-size: .72rem;
  border-left: 3px solid #f0ab00;
  background: #fffbf0;
  padding: .4rem .6rem;
  margin: .4rem 0 0;
}
.lab-solution { border-left-color: #0072a3; background: #f3f8fb; }
.lab-output {
  margin-top: .6rem;
  padding: .5rem .6rem;
  background: #f5f5f5;
  font-size: .62rem;
  line-height: 1.4;
  max-height: 14rem;
  overflow: auto;
  white-space: pre-wrap;
}
.lab-guest {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .6rem;
  padding: .4rem 0;
  border-bottom: 1px solid var(--clr-global-borderColor, #e8e8e8);
}
.lab-guest:last-child { border-bottom: none; }

.lab-setup > summary {
  cursor: pointer;
  font-size: .62rem;
  color: #565656;
}
.lab-setup[open] > summary { margin-bottom: .35rem; }
.lab-setup-warn { color: var(--clr-color-danger-700, #c92100); }
/* A step has six fields and one of them is a whole sentence — side by side they
   would each end up a thumb wide. */
.lab-step-row .lab-field { min-width: 11rem; }

/* The checks of a step: a block of their own inside the step, so it stays
   visible that they belong to it and are worth its points together. */
.lab-checks {
  flex: 1 1 100%;
  grid-column: 1 / -1;
  border-left: 3px solid var(--clr-global-borderColor, #e8e8e8);
  padding-left: .7rem;
  margin-top: .4rem;
}
.lab-check-row {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  align-items: flex-end;
  padding: .35rem 0;
  border-bottom: 1px dotted var(--clr-global-borderColor, #e8e8e8);
}
.lab-check-row:last-child { border-bottom: none; }
.lab-check-row .lab-field { min-width: 9rem; }

/* The exercises sit under the row of columns and take the whole width: a step
   carries a sentence of instruction and, when it fails, the output of the
   guest — both unreadable in a third of the card. */
.lab-exercises {
  border-top: 1px solid var(--clr-global-borderColor, #e8e8e8);
  padding: .6rem 1rem .8rem;
}
.lab-step {
  padding: .45rem 0;
}
.lab-step + .lab-step {
  border-top: 1px dotted var(--clr-global-borderColor, #e8e8e8);
}
.lab-step-head {
  display: flex;
  align-items: center;
  gap: .45rem;
}
/* Fixed width so the titles line up under one another instead of starting at
   three different places because "passed" is wider than "open". */
.lab-step-mark {
  flex: 0 0 4.6rem;
}
.lab-step .form-hint {
  margin: .15rem 0 0 5.05rem;
}
.lab-step-output {
  margin: .3rem 0 0 5.05rem;
  padding: .35rem .5rem;
  max-height: 8rem;
  overflow: auto;
  font-size: .6rem;
  white-space: pre-wrap;
  word-break: break-word;
  background: var(--clr-global-app-background, #fafafa);
  border-left: 2px solid var(--clr-color-danger-700, #c92100);
}

.lab-catalogue-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
}
.lab-editor {
  width: 100%;
  font-family: "Courier New", monospace;
  font-size: .75rem;
}
/* The editor of a description: three sections of rows one can add and throw
   away. A row holds its fields side by side and wraps when the dialog is
   narrow — a lab with six fields per guest must not push the dialog wider. */
.lab-form-switch {
  display: flex;
  justify-content: flex-end;
}
.lab-form-section {
  margin-top: 1rem;
  padding-top: .6rem;
  border-top: 1px solid var(--clr-global-borderColor, #e8e8e8);
}
.lab-form-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: .5rem;
}
.lab-form-head .form-hint { margin: 0; }
.lab-form-head .lab-actions { margin-top: 0; }
.lab-form-grid {
  display: flex;
  flex-wrap: wrap;
  gap: .8rem;
}
.lab-form-grid > .lab-field { flex: 1 1 220px; }
/* A row of fields — networks, links, files, steps.
   Was a wrapping flex row, in which every field took whatever width was left
   over: the columns lined up nowhere, and one field with a hint under it lifted
   its neighbours. A grid gives every field the same width and the same top
   edge, and wraps by whole columns. */
.lab-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  align-items: start;
  gap: .5rem .8rem;
  margin-top: .5rem;
  padding: .55rem .6rem;
  border: 1px solid var(--clr-global-borderColor, #e8e8e8);
  border-radius: .15rem;
  background: #fafafa;
}
/* The bin belongs to the row, not into a column of its own. */
.lab-row > .lab-drop {
  justify-self: end;
  align-self: end;
}

/* ---- One guest in the editor ---------------------------------------------

   Ten fields in one wrapping row put the release next to the gateway and the
   password next to the number of cores — everything ran into everything. A
   guest is now four lines with a caption each: what it is called, where it
   hangs, what it is made of, how big it is, how one gets in. The captions are
   the point: they say why these fields stand together. */
.lab-guest-row {
  margin-top: .6rem;
  padding: .55rem .6rem .7rem;
  border: 1px solid var(--clr-global-borderColor, #e8e8e8);
  border-radius: .15rem;
  background: #fafafa;
}
.lab-guest-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: .6rem;
  padding-bottom: .5rem;
  border-bottom: 1px solid var(--clr-global-borderColor, #e8e8e8);
}
.lab-guest-head-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: .5rem .8rem;
  flex: 1 1 auto;
  min-width: 0;
}
.lab-line {
  display: grid;
  grid-template-columns: 5.2rem minmax(0, 1fr);
  align-items: start;
  gap: .6rem;
  padding: .5rem 0 0;
}
.lab-line + .lab-line {
  margin-top: .35rem;
  border-top: 1px dashed var(--clr-global-borderColor, #e8e8e8);
}
.lab-line-title {
  font-size: .62rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: #767676;
  padding-top: .5rem;
}
.lab-line-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
  align-items: start;
  gap: .5rem .8rem;
  min-width: 0;
}
.lab-guest-extras {
  margin-top: .6rem;
}
/* Narrow: the caption goes above its fields instead of taking a column that
   leaves nothing for them. */
@media (max-width: 640px) {
  .lab-line {
    grid-template-columns: 1fr;
    gap: .2rem;
  }
  .lab-line-title { padding-top: 0; }
}
.lab-field {
  display: flex;
  flex-direction: column;
  flex: 1 1 150px;
  min-width: 0;
  margin: 0;
}
.lab-field > label {
  font-size: .65rem;
  color: #565656;
  margin-bottom: .1rem;
}
.lab-field .clr-input,
.lab-field .clr-select {
  width: 100%;
  min-width: 0;
}
.lab-field .form-hint {
  margin: .1rem 0 0;
  font-size: .6rem;
}
/* The bin at the end of a row, on the line of the fields and not over them. */
.lab-drop {
  flex: 0 0 auto;
  margin: 0 0 .2rem;
  color: #c92100;
}
.lab-guests {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  margin: 0 0 .4rem;
}
/* A guest is a link into its detail page — from the lab into the console in
   one click. */
.lab-guest {
  display: flex;
  align-items: center;
  gap: .4rem;
  padding: .25rem .4rem;
  border: 1px solid var(--clr-global-borderColor, #e8e8e8);
  border-radius: .15rem;
  text-decoration: none;
  color: inherit;
}
.lab-guest:hover {
  background: #f3f9fd;
  border-color: #0072a3;
}
.lab-guest-name {
  flex: 1;
  font-weight: 500;
}
.lab-definition {
  max-height: 60vh;
  overflow: auto;
  font-family: "Courier New", monospace;
  font-size: .72rem;
  background: #fafafa;
  padding: .6rem;
  border: 1px solid var(--clr-global-borderColor, #e8e8e8);
}

/* Sortable column heading. The arrow only appears in the column sorted by —
   a permanent double arrow in every heading is noise. */
.table th.sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.table th.sortable:hover {
  color: #0072a3;
}
.sort-arrow {
  width: .65rem;
  height: .65rem;
  margin-left: .25rem;
  vertical-align: baseline;
}

/* Search bar over a list. Sits between the page header and the table. */
.search-bar {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin: 0 0 .6rem;
}
.search-bar .search-icon {
  color: #767676;
  flex: none;
}
.search-input {
  /* Wide enough for a name plus a word, and not the whole page wide: a search
     field the width of the table reads as if the table itself were meant. */
  width: 100%;
  max-width: 22rem;
}
.search-status {
  font-size: .78rem;
  color: #767676;
  white-space: nowrap;
}

/* Export: the target picker that unfolds under the button.
 *
 * A row per target with its name and one line saying what it is good for. A
 * bare select would show the name only — and "VMDK" against "VHDX" tells the
 * reader nothing who is not at home in all of them. */
.export-panel {
  margin-top: .9rem;
  padding-top: .9rem;
  border-top: 1px solid var(--clr-global-borderColor, #e8e8e8);
}

.export-target {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .1rem .6rem;
  align-items: start;
  padding: .5rem .7rem;
  margin-bottom: .4rem;
  border: 1px solid var(--clr-global-borderColor, #e8e8e8);
  border-radius: .15rem;
  cursor: pointer;
}
.export-target:hover { background: #f7f7f7; }
.export-target.selected { border-color: #0072a3; background: #f3f9fd; }
.export-target input { margin: .25rem 0 0; }
.export-target-label { font-weight: 500; }
.export-target-desc {
  grid-column: 2;
  font-size: .78rem;
  color: #767676;
}

.table-scroll { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-scroll > .table { margin-top: 0; }

/* The hamburger: in the header, but only shown on narrow screens once signed in
   (there is nothing to open on the login page). */
.header-hamburger {
  display: none;
  background: none;
  border: 0;
  color: #fff;
  cursor: pointer;
  padding: 0 .6rem;
  font-size: 1.35rem;
  line-height: 1;
  align-items: center;
}
.nav-backdrop { display: none; }

@media (max-width: 768px) {
  body.signed-in .header-hamburger { display: inline-flex; }

  /* The sidebar slides in over the content instead of taking a fixed column. */
  .phillux-sidenav {
    position: fixed;
    top: var(--clr-header-height, 3rem);
    left: 0;
    bottom: 0;
    width: 250px;
    max-width: 82vw;
    flex-basis: auto;
    z-index: 1001;
    background: var(--clr-global-app-background, #fff);
    box-shadow: 2px 0 10px rgba(0, 0, 0, .18);
    transform: translateX(-100%);
    transition: transform .2s ease;
    overflow-y: auto;
  }
  body.nav-open .phillux-sidenav { transform: none; }

  /* Dims the page behind the open drawer; the element itself takes the click. */
  body.nav-open .nav-backdrop {
    display: block;
    position: fixed;
    inset: var(--clr-header-height, 3rem) 0 0 0;
    background: rgba(0, 0, 0, .42);
    z-index: 1000;
  }

  .phillux-content { padding: 1rem .8rem 3rem; }

  /* Dialogs take almost the whole width. */
  .modal .modal-dialog {
    max-width: none;
    width: calc(100vw - 1.4rem);
    margin: .7rem auto;
  }

  /* Toasts span the width instead of a fixed 380px column at the right. */
  .toast-stack { left: .6rem; right: .6rem; max-width: none; }

  .console-screen { height: 60vh; }
  .page-header h2 { font-size: 1.1rem; }

  /* Nothing may push the page wider than the screen — that would scroll the
     whole content area sideways and clip the left edge (labels, buttons and
     card values disappearing off to the left). The pieces that did it:
     ---------------------------------------------------------------- */
  /* 1) The content never scrolls itself sideways; wide bits below handle their
        own overflow, so there is nothing left to shift the page. */
  .phillux-content { overflow-x: hidden; }
  /* 2) Long unbreakable strings — licence keys, machine ids, SHA fingerprints,
        file paths — wrap instead of forcing the width. */
  .mono, .stat-card .stat-value, .cell-sub, dd, td {
    overflow-wrap: anywhere;
  }
  /* 3) No form field wider than the screen — some carry a min-width (the repo
        address field is 26rem). */
  input, select, textarea, .clr-input, .clr-select {
    min-width: 0 !important;
    max-width: 100%;
  }
  /* 4) Detail-view tab bars (Overview / Console / Snapshots / …) scroll
        sideways instead of overflowing and cutting off the last tab. */
  .phillux-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .phillux-tabs .nav-item { flex: 0 0 auto; }
  /* 5) Vertical info tables (certificate, licence): the label column shrinks
        and wraps instead of holding a fixed 220px. */
  .cert-label { width: auto; white-space: normal; }
}

@media (max-width: 480px) {
  .phillux-content { padding: .8rem .6rem 2.5rem; }
  .console-screen { height: 52vh; }
}

/* ---- Cards in the views ---------------------------------------------------

   The same tidying the dialogues got, applied to the pages: a card is a
   heading, a sentence saying what it is for, the fields, and the buttons at the
   bottom behind a line. It reads in that order because that is the order
   somebody needs it in — what is this, what does it do, what do I fill in, what
   do I press.

   Three things were actually wrong before, and all three are fixed here rather
   than in twenty view files:

   1. The label sat BESIDE the field and smaller than the hint underneath it, so
      "CPU" and "1" ran together into one line of noise. Label above the field,
      one step louder than the hint, exactly as in the dialogues.
   2. Clarity's .clr-row is a twelve-column grid with negative margins. At the
      widths a card actually has, two columns of it either touched or overflowed.
      Inside a card it becomes a plain responsive grid that wraps when it must.
   3. The button hung straight off the last field with nothing between them. It
      now stands in a footer of its own.

   Everything here is scoped to .card so the dialogues, the tables and the
   console are untouched. */

.card > .card-header {
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .01em;
  padding: .75rem 1rem .7rem;
}
.card > .card-block {
  padding: .9rem 1rem 1rem;
}
/* The sentence under the heading. Sits above the fields and reads quieter than
   they do — it explains, it does not ask for anything. */
.card-intro {
  margin: -.15rem 0 .9rem;
  font-size: .72rem;
  line-height: 1.5;
  color: #6b6b6b;
}
.card-block .form-group {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  margin-bottom: .8rem;
}
.card-block .form-group:last-child { margin-bottom: 0; }
.card-block .form-group > label {
  margin: 0 0 .25rem;
  font-size: .7rem;
  font-weight: 600;
  color: #313131;
  line-height: 1.3;
}
/* A checkbox with its text beside it is not a label above a field. */
.card-block .form-group > label > input[type="checkbox"],
.card-block .form-group > label > input[type="radio"] {
  margin-right: .4rem;
}
.card-block .form-group > .clr-input,
.card-block .form-group > .clr-select,
.card-block .form-group > .clr-textarea,
.card-block .form-group > select,
.card-block .form-group > textarea {
  width: 100%;
  max-width: none;
}
.card-block .form-hint {
  font-size: .68rem;
  line-height: 1.5;
  color: #6b6b6b;
  margin-top: .3rem;
}
/* See (2): Clarity's row becomes a grid that wraps by itself. */
.card-block .clr-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0 1.1rem;
  margin: 0;
}
.card-block .clr-row > [class*="clr-col"] {
  padding: 0;
  max-width: none;
  flex: none;
  width: auto;
}
/* The footer: a line, then the buttons. Also picks up a button left standing
   directly in the card body by older code, so no view is left looking odd
   while it waits to be converted. */
.card-actions,
.card-block > .btn:last-child,
.card-block > .btn-group:last-child {
  margin-top: .9rem;
}
.card-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  padding-top: .8rem;
  border-top: 1px solid var(--clr-global-borderColor, #e8e8e8);
}
.card-actions .btn { margin: 0; }
/* A note that belongs to the whole card rather than to one field — it goes
   under the buttons, where it does not push the form down. */
.card-actions + .card-note {
  margin: .6rem 0 0;
  font-size: .68rem;
  color: #6b6b6b;
}
/* Fields that belong together inside one card, with a quiet heading. Same idea
   as .hw-group, which keeps its own name because the hardware block uses it in
   the dialogues too. */
.field-group + .field-group {
  border-top: 1px solid var(--clr-global-borderColor, #e8e8e8);
  margin-top: .9rem;
  padding-top: .8rem;
}
.field-group-title {
  margin: 0 0 .5rem;
  font-size: .68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: #565656;
}
