/* Fonts and colors follow moin-website/src/styles/site.css. danger, warning and
   surface are not there; they are the app palette's, which site.css names as its
   source: moin-flutter/lib/shared/theme/palettes/moin.dart. */

@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/manrope-400-700.woff2') format('woff2');
}

@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/dm-mono-400.woff2') format('woff2');
}

@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/dm-mono-500.woff2') format('woff2');
}

/* Each color is written once, light then dark, and the system picks. */
:root {
  color-scheme: light dark;
  --page: light-dark(#f2f5fa, #080c12);
  --ink: light-dark(#202c40, #f3f6fb);
  --muted-bright: light-dark(#58677e, #aab4c3);
  --muted: light-dark(#58677e, #8693a6);
  --line: light-dark(#cdd6e6, #263241);
  --line-soft: light-dark(#e3e8f0, rgba(112, 131, 155, 0.18));
  --periwinkle: light-dark(#4561b8, #7da2ff);
  --periwinkle-bright: light-dark(#4561b8, #a7bcff);
  --sea-glass: light-dark(#277657, #48d5aa);
  --danger: light-dark(#b3344b, #f08a96);
  --warning: light-dark(#8b5b09, #ffc66d);
  --nav-surface: light-dark(#e8edf5, #080c12);
  --menu-surface: light-dark(#fdfeff, #171d26);
  --surface: light-dark(#fffffff2, #111822f2);
  --shadow: light-dark(#0d244518, #0006);
  --button-background: light-dark(#4561b8, #f3f6fb);
  --button-ink: light-dark(#fff, #080c12);
  --button-hover: light-dark(#3a529c, #cbd8ff);
  --font-body: 'Manrope', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  --font-utility: 'DM Mono', ui-monospace, SFMono-Regular, Consolas, monospace;
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

/* Only the gallery sets these, to show a page in the theme the system is not in. */
:root[data-theme='light'] {
  color-scheme: light;
}
:root[data-theme='dark'] {
  color-scheme: dark;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  background: var(--page);
}

/* Operators read these pages all day: mono and tight. */
body {
  min-height: 100vh;
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font: 400 13px/1.5 var(--font-utility);
}

/* Customers read theirs once: running text in the website's face, with room. #page
   holds the frame, so that htmx swapping the body's content brings who reads it too. */
.customers {
  font: 400 14px/1.6 var(--font-body);
}

a {
  color: var(--periwinkle-bright);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
  text-underline-offset: 0.25em;
}

::selection {
  background: color-mix(in srgb, var(--periwinkle) 38%, transparent);
  color: var(--ink);
}

:focus-visible {
  outline: 2px solid var(--periwinkle-bright);
  outline-offset: 2px;
}

/* The frame. */

header {
  position: sticky;
  z-index: 10;
  top: 0;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  height: 40px;
  padding: 0 1.25rem;
  border-bottom: 1px solid var(--line-soft);
  background: var(--nav-surface);
  font: 400 13px/1.5 var(--font-utility);
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--ink);
  font-weight: 500;
  white-space: nowrap;
}
.brand:hover {
  text-decoration: none;
}
.brand img {
  width: 20px;
  height: 20px;
}
.brand span {
  padding: 1px 6px;
  border: 1px solid var(--line);
  border-radius: 3px;
  color: var(--muted-bright);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

header nav {
  display: flex;
  flex: 1;
  align-items: center;
  gap: 0.25rem;
}
header nav a {
  display: flex;
  align-items: center;
  height: 26px;
  padding: 0 0.6rem;
  border-radius: 3px;
  color: var(--muted-bright);
  white-space: nowrap;
}
header nav a:hover {
  color: var(--ink);
  text-decoration: none;
}
header nav a[aria-current] {
  background: color-mix(in srgb, var(--periwinkle) 16%, transparent);
  color: var(--ink);
}
header nav .split {
  width: 1px;
  height: 14px;
  margin: 0 0.5rem;
  background: var(--line);
}
header .who {
  color: var(--muted);
  white-space: nowrap;
}

/* What is too wide and has no sheet to scroll in scrolls here, not the page. */
main {
  max-width: 88rem;
  margin: 0 auto;
  padding: 0 1.25rem 5rem;
  overflow-x: auto;
}
.customers main {
  max-width: 52rem;
}
/* One thing to do, in the middle of the window. */
.alone main {
  max-width: 26rem;
  padding-top: 3rem;
}
.alone form.stack {
  max-width: none;
}

/* What heads a page: its title, and beside it what the page puts there. */
.title {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.9rem;
  align-items: center;
  padding: 1.1rem 0 0.8rem;
}
.customers .title {
  padding: 2rem 0 1rem;
}
h1 {
  margin: 0;
  font: 500 15px/1.4 var(--font-utility);
}
.customers h1 {
  font: 600 22px/1.3 var(--font-body);
  letter-spacing: -0.03em;
}

/* The way back, over the title. */
.crumb {
  flex-basis: 100%;
  margin: 0;
  color: var(--muted);
  font-size: 12px;
}
.crumb a {
  color: inherit;
}
.crumb a:hover {
  color: var(--ink);
}

/* Where a thing is, in a few words each, at the title's far end. */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-left: auto;
}
.chips span {
  padding: 2px 8px;
  border: 1px solid var(--line-soft);
  border-radius: 3px;
  color: var(--muted);
  font-size: 12px;
  white-space: nowrap;
}
.chips b {
  margin-left: 0.2rem;
  color: var(--ink);
  font-weight: 400;
}

h2 {
  margin: 1.6rem 0 0.4rem;
  color: var(--muted);
  font: 400 11px/1.5 var(--font-utility);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
h2 small {
  margin-left: 0.6rem;
  font-size: 10px;
  letter-spacing: 0;
  text-transform: none;
}

p {
  margin: 0.5rem 0;
}

.muted {
  color: var(--muted);
}

/* Tables and sheets. A sheet is the bordered surface a table sits on, and scrolls
   sideways when the table is too wide; a table with no sheet around it draws its own
   border. */

.sheet {
  overflow-x: auto;
  border: 1px solid var(--line-soft);
  border-radius: 4px;
  background: var(--surface);
}

table {
  width: 100%;
  border: 1px solid var(--line-soft);
  border-radius: 4px;
  border-spacing: 0;
  background: var(--surface);
}
.sheet > table {
  border: 0;
  border-radius: 0;
  background: none;
}
table:empty {
  display: none;
}

/* A feed says "None." until it has a row; a live feed's first row can arrive. */
.feed:has(tbody > tr) > .none,
.feed:not(:has(tbody > tr)) > .sheet {
  display: none;
}

th,
td {
  padding: 5px 0.8rem;
  border-bottom: 1px solid var(--line-soft);
  text-align: left;
  vertical-align: top;
  white-space: nowrap;
}
tbody > tr:last-child > * {
  border-bottom: 0;
}
td {
  font: 400 13px/1.5 var(--font-utility);
}
tr:hover > td {
  background: color-mix(in srgb, var(--periwinkle) 6%, transparent);
}

/* A column's head. */
th {
  color: var(--muted);
  font: 400 11px/1.7 var(--font-utility);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
tr:has(> th + th) > th {
  background: color-mix(in srgb, var(--page) 50%, transparent);
}

/* A row's label, and the value beside it. */
th:has(+ td) {
  width: 13rem;
  color: var(--muted-bright);
  font-size: 13px;
  line-height: 1.5;
  letter-spacing: 0;
  text-transform: none;
}

/* Text and JSON are what a table has room for: their column takes the width the others
   leave, wraps at its spaces, and inside a string only when one alone is wider than the
   cell. With no such column the last one takes what is left, so the others stay as wide
   as what is in them. */
td.text {
  width: 100%;
  min-width: 14rem;
  white-space: normal;
  overflow-wrap: anywhere;
}
.sheet > table:not(.state, :has(.text)) :is(th, td):last-child {
  width: 100%;
}

/* A table with no sheet still reads: a value beside its label wraps, and the last
   column takes what is left. */
table:not(.sheet > *) th + td {
  white-space: normal;
  overflow-wrap: anywhere;
}
table:not(.sheet > *) :is(th, td):last-child {
  width: 100%;
}

pre,
code {
  color: var(--muted-bright);
  font: inherit;
  font-family: var(--font-utility);
}
pre {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
details summary {
  color: var(--muted-bright);
  cursor: pointer;
}

/* What is shown once: an administrator link, a registration token. */
.secret {
  padding: 0.75rem;
  border: 1px solid var(--periwinkle);
  border-radius: 4px;
  background: var(--surface);
  font: 400 13px/1.5 var(--font-utility);
  overflow-wrap: anywhere;
}

td.soft {
  color: var(--muted-bright);
}
.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Desired beside observed: the subject, then a column each, sharing the width. */
table.state {
  min-width: 40rem;
  table-layout: fixed;
}
table.state :is(th, td) {
  width: auto;
  min-width: 0;
  white-space: normal;
}
table.state th:first-child {
  width: 10rem;
}
tr.differs > * {
  background: color-mix(in srgb, var(--warning) 7%, transparent);
}

/* What to know about the value it follows. */
.note {
  display: inline-block;
  margin-left: 0.75rem;
  color: var(--warning);
  font-size: 12px;
}

/* A thing's facts, three to a row; the gaps draw the lines, so any number of columns
   closes. */
.facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  margin: 0;
  background: var(--line-soft);
}
.facts div {
  padding: 0.45rem 0.8rem;
  background: var(--surface);
}
.facts dt {
  color: var(--muted);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.facts dd {
  margin: 0;
  overflow: hidden;
  font: 400 13px/1.5 var(--font-utility);
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Two blocks side by side, the first taking the room. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0 1rem;
  align-items: start;
}

/* What is used of what there is: the numbers, and a bar that warns from four fifths. */
.meter {
  display: inline-grid;
  grid-template-columns: 4.5rem 5rem;
  gap: 0.6rem;
  align-items: center;
  white-space: nowrap;
}
.meter > span {
  text-align: right;
}
.meter em {
  color: var(--muted);
  font-style: normal;
}
.meter i {
  height: 4px;
  overflow: hidden;
  border-radius: 2px;
  background: var(--line-soft);
}
.meter i::after {
  display: block;
  width: var(--filled);
  height: 100%;
  border-radius: inherit;
  background: var(--periwinkle);
  content: '';
}
.meter i.high::after {
  background: var(--warning);
}
/* One meter on its own has no column of numbers to line up with. */
:is(table.state, .facts) .meter {
  grid-template-columns: auto 5rem;
}

/* What a page waits for, under the line that names it: the meter's bar, running across
   as the website's does. A reader who asked for less motion has the line alone, since
   a bar standing still would read as a meter. */
.loader {
  width: 8rem;
  height: 4px;
  margin: 0.9rem 0 1.1rem;
  overflow: hidden;
  border-radius: 2px;
  background: var(--line-soft);
}
.loader::after {
  display: block;
  width: 40%;
  height: 100%;
  border-radius: inherit;
  background: var(--periwinkle);
  animation: loader 1.8s ease-in-out infinite;
  content: '';
}
@keyframes loader {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(350%);
  }
}
@media (prefers-reduced-motion: reduce) {
  .loader {
    display: none;
  }
}

/* The version a tenant runs, after the one it is to run. */
.was {
  margin-left: 0.5rem;
  color: var(--warning);
}

/* A list's filter: links in one bar, at the title's far end. */
nav.filter {
  display: flex;
  flex-wrap: wrap;
  width: fit-content;
  padding: 2px;
  border: 1px solid var(--line-soft);
  margin-left: auto;
  border-radius: 4px;
  background: var(--surface);
}
nav.filter a {
  padding: 2px 10px;
  border-radius: 3px;
  color: var(--muted-bright);
  font-size: 12px;
  text-transform: capitalize;
  white-space: nowrap;
}
nav.filter a span {
  margin-left: 0.1rem;
  color: var(--muted);
}
nav.filter a:hover {
  color: var(--ink);
  text-decoration: none;
}
nav.filter a[aria-current] {
  background: color-mix(in srgb, var(--periwinkle) 18%, transparent);
  color: var(--ink);
}

/* Statuses. status-<word> alone colors its text; with status it marks the word. */

[class*='status-'] {
  --status: var(--muted);
  color: var(--status);
}
.status-active,
.status-ready,
.status-done,
.status-current {
  --status: var(--sea-glass);
}
.status-provisioning,
.status-creating,
.status-bootstrapping,
.status-queued,
.status-running,
.status-upgrading {
  --status: var(--periwinkle);
}
.status-suspended,
.status-draining,
.status-behind {
  --status: var(--warning);
}
.status-failed,
.status-deleted {
  --status: var(--danger);
}

.status {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  color: inherit;
  white-space: nowrap;
}
.status::before {
  width: 6px;
  height: 6px;
  border-radius: 1px;
  background: var(--status);
  content: '';
}

/* Controls. */

button,
input,
select {
  height: 26px;
  padding: 0 0.65rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  color: inherit;
  font: 400 12px/1 var(--font-utility);
}
button {
  background: color-mix(in srgb, var(--menu-surface) 60%, transparent);
  white-space: nowrap;
  cursor: pointer;
}
button:hover {
  border-color: color-mix(in srgb, var(--periwinkle) 55%, var(--line));
  background: color-mix(in srgb, var(--periwinkle) 12%, transparent);
}
input,
select {
  background: var(--page);
  font-size: 13px;
}
input:focus,
select:focus {
  border-color: var(--periwinkle);
  outline: none;
}
input::placeholder {
  color: var(--muted);
  opacity: 0.6;
}
input[type='checkbox'] {
  width: 14px;
  height: 14px;
  padding: 0;
  accent-color: var(--periwinkle);
}
td button {
  height: 20px;
  padding: 0 0.5rem;
}

/* Customers' controls have the room their pages do. */
.customers :is(button, input, select) {
  height: 32px;
  font-size: 13px;
}
.customers main button {
  padding: 0 0.9rem;
  font: 600 13px/1 var(--font-body);
}

/* A link that is the thing to do: New community. */
a.button {
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0 0.9rem;
  border-radius: 3px;
  background: var(--button-background);
  color: var(--button-ink);
  font: 600 13px/1 var(--font-body);
  white-space: nowrap;
}
a.button:hover {
  background: var(--button-hover);
  text-decoration: none;
}
.title a.button {
  margin-left: auto;
}

/* A button that reads as a link: Sign out. */
button.link {
  height: auto;
  padding: 0;
  border: 0;
  background: none;
  color: var(--muted-bright);
  font-size: 13px;
}
button.link:hover {
  background: none;
  color: var(--ink);
}

/* Forms. */

label {
  display: block;
  color: var(--muted);
  font: 400 11px/1.5 var(--font-utility);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
label input,
label select {
  display: block;
  width: 100%;
  margin-top: 0.2rem;
  color: var(--ink);
  letter-spacing: 0;
}
label code {
  color: var(--ink);
  letter-spacing: 0;
  text-transform: none;
}

/* A checkbox and what it means, and a field with words after it, sit in a line. */
label:has(> input[type='checkbox']),
label > span {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--muted-bright);
  font-size: 12px;
  letter-spacing: 0;
  text-transform: none;
}
label:has(> input[type='checkbox']) {
  min-height: 26px;
}
label:has(> input[type='checkbox']) input,
label > span input {
  margin-top: 0;
}
label > span input {
  flex: 1;
  width: auto;
}

form.inline,
.actions form {
  display: inline-flex;
  gap: 0.4rem;
  align-items: end;
}
main > form.inline,
main > section > form.inline {
  margin-bottom: 0.6rem;
}

/* One form under another, its button the thing to do. */
form.stack {
  display: grid;
  gap: 0.6rem;
  max-width: 28rem;
  padding: 0.8rem;
  border: 1px solid var(--line-soft);
  border-radius: 4px;
  background: var(--surface);
}
/* Its fields in one row, in a sheet, going to further rows as the window slims. */
form.row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: end;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--line-soft);
  border-radius: 4px;
  background: var(--surface);
}
form.row > label {
  flex: 1 1 12rem;
}
form.row > button {
  min-width: 6rem;
}

/* A link beside the button, such as a filter's Clear. */
form.row > a {
  line-height: 26px;
  white-space: nowrap;
}

:is(form.stack, form.row) > button {
  border-color: transparent;
  background: var(--button-background);
  color: var(--button-ink);
}
:is(form.stack, form.row) > button:hover {
  background: var(--button-hover);
}

/* A page's actions in one bar. One with nothing to fill in is a button; one with
   fields opens its form under its name, and naming the <details> alike keeps one open
   at a time. */
.cmd {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
  padding: 0.4rem;
  border: 1px solid var(--line-soft);
  border-radius: 4px;
  background: var(--surface);
}
.cmd > form {
  display: contents;
}
.cmd .rule {
  width: 1px;
  height: 18px;
  margin: 0 0.25rem;
  background: var(--line);
}
.cmd details {
  position: relative;
}
.cmd details:last-child {
  margin-left: auto;
}
.cmd summary {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  height: 26px;
  padding: 0 0.65rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: color-mix(in srgb, var(--menu-surface) 60%, transparent);
  font-size: 12px;
  list-style: none;
  white-space: nowrap;
  cursor: pointer;
}
.cmd summary::-webkit-details-marker {
  display: none;
}
.cmd summary::after {
  width: 5px;
  height: 5px;
  margin-top: -3px;
  border-right: 1.5px solid var(--muted);
  border-bottom: 1.5px solid var(--muted);
  transform: rotate(45deg);
  content: '';
}
.cmd summary:hover {
  border-color: color-mix(in srgb, var(--periwinkle) 55%, var(--line));
  background: color-mix(in srgb, var(--periwinkle) 12%, transparent);
}
.cmd details[open] summary {
  border-color: var(--periwinkle);
}
.cmd details form {
  position: absolute;
  z-index: 5;
  top: calc(100% + 6px);
  left: 0;
  width: 19rem;
  border-color: var(--line);
  background: var(--menu-surface);
  box-shadow: 0 12px 32px var(--shadow);
}
.cmd details:last-child form {
  right: 0;
  left: auto;
}
.cmd details p {
  margin: 0;
  color: var(--muted-bright);
  font-size: 12px;
}

/* What cannot be undone. */
button.danger,
.danger summary,
.danger form.stack > button {
  border-color: color-mix(in srgb, var(--danger) 45%, var(--line));
  color: var(--danger);
}
.danger form.stack > button {
  background: none;
}
button.danger:hover,
.danger form.stack > button:hover {
  background: color-mix(in srgb, var(--danger) 14%, transparent);
}

/* What is wrong now, in a line. */
.failing {
  color: var(--danger);
}

/* A filter form over the table it filters. */
form.filters {
  margin-bottom: 0.6rem;
}

/* Links between a long list's pages, under it. */
nav.pages {
  display: flex;
  gap: 1rem;
  justify-content: flex-end;
  margin-top: 0.6rem;
  font: 400 12px/1.5 var(--font-utility);
  white-space: nowrap;
}

/* A page's actions, in a bar that wraps. */
div.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  align-items: end;
  padding: 0.5rem;
  border: 1px solid var(--line-soft);
  border-radius: 4px;
  background: var(--surface);
}
td.actions {
  white-space: nowrap;
}
td.actions > :not(:first-child) {
  margin-left: 0.3rem;
}

/* Slim windows. The header's one row needs about 66rem when it has the operators'
   links, and about 40rem without them; under that the links take a second row and the
   header scrolls away with the page. */

/* Jobs beside Backups needs about 86rem; under that they stack. */
@media (max-width: 86rem) {
  .split {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 66rem) {
  nav.filter,
  .chips {
    flex-basis: 100%;
    margin-left: 0;
  }
  nav.filter {
    flex-basis: auto;
  }
  .title {
    row-gap: 0.5rem;
  }
  .facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  header:has(.split) {
    position: static;
    flex-wrap: wrap;
    gap: 0 1rem;
    height: auto;
    padding-top: 7px;
  }
  header:has(.split) nav {
    flex: 1 0 100%;
    flex-wrap: wrap;
    order: 3;
    margin: 0 -0.6rem;
    padding: 4px 0;
  }
  header:has(.split) .who {
    margin-left: auto;
  }
  header .split {
    display: none;
  }
}

@media (max-width: 40rem) {
  header {
    position: static;
    flex-wrap: wrap;
    gap: 0 0.75rem;
    height: auto;
    padding-top: 7px;
  }
  header nav {
    flex: 1 0 100%;
    flex-wrap: wrap;
    order: 3;
    margin: 0 -0.6rem;
    padding: 4px 0;
  }
  header,
  main {
    padding-inline: 0.75rem;
  }
  header .who {
    display: none;
  }
  header form {
    margin-left: auto;
  }
  header:not(:has(form)) nav {
    display: none;
  }
  .facts {
    grid-template-columns: minmax(0, 1fr);
  }
  /* A form opens across the bar, not from its own name, so it cannot hang off the
     screen. */
  .cmd {
    position: relative;
  }
  .cmd details {
    position: static;
  }
  .cmd details:last-child {
    margin-left: 0;
  }
  .cmd details form,
  .cmd details:last-child form {
    right: 0;
    left: 0;
    width: auto;
  }
  .cmd .rule {
    display: none;
  }
}
