@import url("fonts.css");

/* The palette of intervall.io: near black on white, inverted when the system
   asks for dark. --accent is overridden per customer on their own pages. */
:root {
  --black: #111;
  --white: #fff;
  --muted: #767676;
  --line: #dcdcdc;
  /* Interaction green, the primary colour of the spec tool. Overridden per
     customer. Errors have their own colour so a hover never looks like a
     warning. */
  --accent: #46af28;
  --error: #ff5900;
  --mono: "Source Code Pro", ui-monospace, SFMono-Regular, Menlo, monospace;
  --serif: "Source Serif 4", Georgia, "Times New Roman", serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --black: #ededed;
    --white: #111;
    --muted: #8f8f8f;
    --line: #2e2e2e;
  }
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font: 400 15px/24px var(--mono);
  color: var(--black);
  background: var(--white);
  -webkit-text-size-adjust: 100%;
}

a {
  color: inherit;
}

/* ── Portal ─────────────────────────────────────────────────────────────── */

.wrapper {
  min-height: 100vh;
  margin-inline: 48px;
  padding: 40px 0 0;
  display: flex;
  flex-direction: column;
}

header {
  margin-bottom: 80px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

header a {
  display: inline-block;
}

/* ── Menu ───────────────────────────────────────────────────────────────── */

.menu {
  position: relative;
}

/* Three hairlines, same weight as the rules that structure the page. */
.menu summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 8px 0 8px 24px;
}

.menu summary::-webkit-details-marker {
  display: none;
}

.menu summary span {
  display: block;
  width: 20px;
  height: 1px;
  background: var(--black);
}

.menu nav {
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  min-width: 200px;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 4px 16px;
  z-index: 10;
}

.menu nav a {
  display: block;
  font: 400 14px/20px var(--mono);
  text-decoration: none;
  padding: 10px 0;
  border-top: 1px solid var(--line);
}

.menu nav a:first-child {
  border-top: 0;
}

.menu nav a:hover {
  font-weight: 600;
}

.menu nav form {
  margin: 0;
  border-top: 1px solid var(--line);
}

.menu nav button.menu-link {
  width: 100%;
  margin: 0;
  padding: 10px 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: inherit;
  font: 400 14px/20px var(--mono);
  text-align: left;
  box-shadow: none;
}

.menu nav button.menu-link:hover {
  font-weight: 600;
}

.logo {
  height: 16px;
  width: auto;
  display: block;
}

.logo.customer {
  height: 28px;
}

/* Scoped to the portal: the sign in card is a main too and must keep its own
   height instead of being stretched. */
.wrapper main {
  flex: 1 0 auto;
}

/* The ladder of intervall.io: big serif headings over mono body text. */
h1 {
  font: 400 42px/52px var(--serif);
  margin-bottom: 8px;
}

h2 {
  font: 400 28px/36px var(--serif);
  margin-bottom: 16px;
}

h3 {
  font: 400 20px/28px var(--serif);
  margin-bottom: 8px;
}

.meta,
.hint,
.empty {
  color: var(--muted);
}

.meta {
  margin-bottom: 56px;
}

/* The admin tabs. Deliberately not shaped like the filter chips further down
   the page: those narrow a table, these change the page. One hairline runs
   under all of them, the current one sits on a darker piece of it. */
.tabs {
  display: flex;
  gap: 32px;
  margin: 24px 0 32px;
  border-bottom: 1px solid var(--line);
}

.tabs a {
  font: 400 13px/16px var(--mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
  padding-bottom: 12px;
  margin-bottom: -1px;
  border-bottom: 1px solid transparent;
}

.tabs a:hover {
  color: var(--black);
}

.tabs a.is-active {
  color: var(--black);
  border-bottom-color: var(--black);
}

section {
  margin-bottom: 56px;
}

/* The org label on the overview rides above the first hairline like a column
   header. It labels the rows below it, so it stays small; a heading here
   would fight with the serif row names. */
.label {
  font: 400 12px/16px var(--mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 8px;
}

/* One application per line: name in serif, meta small and grey to the right,
   separated by hairlines across the full width. */
.row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  padding: 16px 0;
  border-top: 1px solid var(--line);
  text-decoration: none;
}

section .row:last-child {
  border-bottom: 1px solid var(--line);
}

.row-name {
  font: 400 28px/36px var(--serif);
}

.row-meta {
  font: 400 12px/16px var(--mono);
  color: var(--muted);
  text-align: right;
  flex-shrink: 0;
  max-width: 22rem;
}

.row:hover .row-name {
  font-weight: 600;
}

/* The name of a project and the third-order button that belongs to it sit on one
   baseline, the button right of the name. */
.label-row {
  display: flex;
  align-items: baseline;
  gap: 16px;
  margin-bottom: 8px;
}

.label-row .label,
.label-row h1 {
  margin-bottom: 0;
}

/* The name of a project leads to its own page. It stays a label, so the link
   carries no underline until the pointer is on it. */
.label a {
  color: inherit;
  text-decoration: none;
}

.label a:hover {
  color: var(--black);
  text-decoration: underline;
}

.title-row {
  gap: 24px;
  margin-bottom: 8px;
}

/* The same shape as the menu in the header: a details whose panel hangs under
   the word. No script, and closed it costs nothing. */
.links {
  position: relative;
}

.links summary {
  list-style: none;
  cursor: pointer;
}

.links summary::-webkit-details-marker {
  display: none;
}

/* Third order: not a button and not a row, a word beside the name. */
.links-toggle {
  font: 400 12px/16px var(--mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.links summary:hover .links-toggle,
.links[open] .links-toggle {
  color: var(--black);
}

.links-toggle .count {
  font-variant-numeric: tabular-nums;
}

.popover {
  position: absolute;
  left: 0;
  top: calc(100% + 8px);
  z-index: 10;
  width: max-content;
  max-width: min(30rem, 80vw);
  /* A project low on the page would push its panel past the window edge, so the
     panel scrolls instead of being cut off. */
  max-height: min(70vh, 40rem);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 4px 20px 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
}

/* Says what the list is. These addresses leave the platform, an app row does
   not, and the reader has to see the difference before clicking. */
.popover-head {
  font: 400 12px/16px var(--mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--black);
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.popover-head span {
  text-transform: none;
  letter-spacing: 0;
  color: var(--muted);
}

.link-row {
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.link-row:last-child {
  border-bottom: 0;
}

.link-name {
  font: 400 18px/26px var(--serif);
  text-decoration: none;
}

.link-name:hover {
  text-decoration: underline;
}

/* Where it leads, in the address itself: that is what tells prod from stg. */
.link-host,
.link-note,
.link-reach {
  display: block;
  font: 400 12px/18px var(--mono);
  color: var(--muted);
}

.link-host {
  overflow-wrap: anywhere;
}

.link-reach {
  color: var(--black);
}

/* The other environments of the same system, behind the name, so a click
   without looking lands on production. */
.envs {
  font: 400 12px/16px var(--mono);
  color: var(--muted);
}

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

.envs a:hover {
  color: var(--black);
}

footer {
  flex-shrink: 0;
  margin-top: 80px;
  /* The same 24px that intervall.io keeps below its footer. It also holds the
     last line out of the bottom left corner, where the browser writes the
     target of every hovered link over the page. */
  padding-bottom: 24px;
  font: 400 12px/20px var(--mono);
  color: var(--muted);
}

footer .actions {
  margin-bottom: 4px;
}

footer a {
  color: var(--muted);
}

footer a:hover,
.card-links a:hover {
  color: var(--black);
}

/* Body type in full black, the links underlined at a distance, the underline
   gone on hover: the footer of intervall.io, in the type of this page. */
.legal {
  font: 400 15px/24px var(--mono);
  color: var(--black);
}

.legal a {
  color: inherit;
  text-underline-offset: 4px;
}

.legal a:hover {
  text-decoration: none;
}

/* The sign in page centres the line: there is a card in the middle and nothing
   beside it. */
.auth-footer .legal {
  text-align: center;
}

/* ── Admin ──────────────────────────────────────────────────────────────── */

/* Title left, filters right on the same baseline, like a column header row. */
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 24px;
}

.section-head h2 {
  margin-bottom: 0;
}

.section-meta {
  font: 400 12px/20px var(--mono);
  color: var(--muted);
}

.warn {
  color: var(--error);
}

/* The one number a glance needs, its context small below it. */
.stats {
  display: flex;
  gap: 56px;
  flex-wrap: wrap;
  margin: 8px 0 32px;
}

.stat-value {
  display: block;
  font: 600 20px/28px var(--mono);
  font-variant-numeric: tabular-nums;
}

.stat-label {
  display: block;
  font: 400 12px/16px var(--mono);
  color: var(--muted);
}

.filterbar {
  display: flex;
  align-items: baseline;
  gap: 24px;
}

/* A filterbar that stands on its own, below the numbers rather than next to
   the heading, needs the gap the section head would have given it. Without it
   the search field sits right on top of the column headers. */
.filterbar.standalone {
  margin-bottom: 24px;
}

.filterbar input {
  width: 240px;
  margin: 0;
  font: 400 12px/20px var(--mono);
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  padding: 2px 0;
  background: transparent;
}

.filterbar input:focus {
  outline: none;
  border-color: var(--accent);
}

.chips {
  display: flex;
  gap: 16px;
}

.chip {
  width: auto;
  font: 400 12px/20px var(--mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
}

.chip:hover {
  color: var(--black);
  filter: none;
}

.chip.is-active {
  color: var(--black);
  font-weight: 600;
}

.pager {
  display: flex;
  align-items: baseline;
  gap: 24px;
  flex-wrap: wrap;
  margin-top: 8px;
  font: 400 12px/20px var(--mono);
  color: var(--muted);
}

.pager-info {
  margin-right: auto;
}

.pager-pages {
  gap: 12px;
}

.pager-size {
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.pager select {
  font: 400 12px/20px var(--mono);
  color: var(--black);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  padding: 2px 0;
  margin-left: 8px;
  cursor: pointer;
}

/* Same reading direction as the rows above: everything left aligned,
   hairlines between the lines, numbers kept comparable by tabular figures. */
.table {
  width: 100%;
  border-collapse: collapse;
  font: 400 14px/20px var(--mono);
  font-variant-numeric: tabular-nums;
}

.table th {
  font: 400 12px/16px var(--mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: left;
  font-weight: 400;
  padding: 0 24px 8px 0;
  border-bottom: 1px solid var(--line);
}

.table td {
  vertical-align: top;
  text-align: left;
  padding: 12px 24px 12px 0;
  border-bottom: 1px solid var(--line);
}

.table th:last-child,
.table td:last-child {
  padding-right: 0;
}

.meta-cell {
  display: block;
  font: 400 12px/16px var(--mono);
  color: var(--muted);
}

/* Names, not essays: the effective people stand here, the shipped list is a
   one line note, so the column stays narrow. */
.who-cell {
  max-width: 20rem;
}

/* A banned list can run long. Kept scrollable so the jail table stays the
   height of a table and not the height of a ban list. */
.addresses {
  display: block;
  max-height: 8rem;
  overflow-y: auto;
}

/* User agent strings are written to be long. One line, cut where the column
   ends: the full string is in the log, the shape is what is read here. */
.agent {
  display: block;
  max-width: 16rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lock {
  width: 16px;
  height: 16px;
  color: var(--muted);
  margin-left: 6px;
  vertical-align: -2px;
}

.row-name .lock {
  width: 20px;
  height: 20px;
  margin-left: 10px;
}

.meta-cell .lock {
  width: 12px;
  height: 12px;
  margin: 0 2px 0 0;
  vertical-align: -2px;
}

/* The app password page: the auth card's form language on an admin page. */
.narrow {
  max-width: 360px;
}

.narrow .linkish {
  margin-top: 8px;
}

.table-note {
  margin-top: 8px;
  font: 400 12px/18px var(--mono);
  color: var(--muted);
}

/* A folded count in a cell; the click reveals the names. */
.expand summary {
  list-style: none;
  cursor: pointer;
  width: fit-content;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--line);
}

.expand summary::-webkit-details-marker {
  display: none;
}

.expand summary:hover {
  text-decoration-color: var(--black);
}

.expand[open] summary {
  font-weight: 600;
  text-decoration: none;
}

.expand .meta-cell {
  margin-top: 4px;
}

.actions-cell form {
  display: block;
}

/* An action inside a table is a link, not a green primary button. */
button.linkish {
  width: auto;
  font: 400 12px/20px var(--mono);
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0;
  text-decoration: underline;
  cursor: pointer;
}

button.linkish:hover {
  color: var(--black);
  filter: none;
}

/* ── Sign in ────────────────────────────────────────────────────────────── */

body.auth {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.card {
  width: 360px;
  max-width: 100%;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 32px 24px;
  text-align: center;
  background: var(--white);
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
}

.card-logo {
  display: flex;
  justify-content: center;
  margin-bottom: 24px;
}

.card .logo {
  height: 16px;
}

.card .logo.customer {
  height: 32px;
}

.card h1 {
  font: 600 15px/24px var(--mono);
  letter-spacing: 0.04em;
  text-transform: none;
  margin-bottom: 8px;
}

.card .hint {
  font: 400 12px/18px var(--mono);
  margin-bottom: 24px;
}

.card form {
  margin-top: 8px;
}

input {
  width: 100%;
  font: 400 14px/20px var(--mono);
  color: var(--black);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 8px 12px;
  margin-bottom: 12px;
}

input::placeholder {
  color: var(--muted);
}

input:focus {
  outline: none;
  border-color: var(--accent);
}

button {
  width: 100%;
  font: 600 14px/20px var(--mono);
  color: #fff;
  background: var(--accent);
  border: 0;
  border-radius: 4px;
  padding: 8px 16px;
  margin-top: 4px;
  cursor: pointer;
}

button:hover {
  filter: brightness(0.92);
}

.error {
  font: 400 12px/18px var(--mono);
  color: var(--error);
  margin-bottom: 12px;
}

.card-links {
  font: 400 12px/18px var(--mono);
  color: var(--muted);
  margin-top: 16px;
}

.card-links a {
  color: var(--muted);
}

/* Asking for a new code sends a mail, so it is a form and not a link. In the
   line under the card it still has to read as one. */
.card-links form {
  display: inline;
}

.card-links button.link {
  /* Undoes the primary button above, which is full width by design. */
  width: auto;
  margin: 0;
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  text-decoration: underline;
  cursor: pointer;
}

.card-links button.link:hover {
  color: var(--black);
}

.auth-footer {
  margin-top: 24px;
  font: 400 12px/20px var(--mono);
  color: var(--muted);
  text-align: center;
}

.auth-footer a {
  color: var(--muted);
}

@media (max-width: 40rem) {
  .wrapper {
    margin-inline: 24px;
  }

  header {
    margin-bottom: 48px;
  }

  h1 {
    font-size: 32px;
    line-height: 40px;
  }

  h2 {
    font-size: 24px;
    line-height: 32px;
  }

  .row {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }

  .row-name {
    font-size: 22px;
    line-height: 30px;
  }

  .row-meta {
    text-align: left;
    max-width: none;
  }
}

/* ── Access for machines ────────────────────────────────────────────────── */

/* The way back: the chevron hangs left of the heading, in the margin, and it
   carries the muted colour until the pointer is on it. */
.back-row {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-left: -40px;
}

.back {
  display: inline-flex;
  color: var(--muted);
  flex: 0 0 auto;
}

.back:hover {
  color: var(--black);
}

.back .icon {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
}

/* The first portal page with running text on it. It gets a measure, the rest
   of the portal is lists and tables that use the whole window. */
.prose {
  max-width: 46rem;
  margin-bottom: 24px;
}

/* Black, like every action in the tools. The green is the sign in card's, and
   it came from the spec tool. */
main form.one-action button {
  background: var(--black);
}

/* A snippet somebody copies into a file or a terminal. It wraps instead of
   scrolling sideways: a block that hides its right half is one nobody can copy
   safely, and these are mostly short lines of YAML and shell. */
/* A page that is read rather than scanned, so it stays one column at the width
   of its own prose. The section heads elsewhere push their meta to the far
   right, which over an empty half of a wide screen reads as a layout mistake. */
.doc {
  max-width: 46rem;
}

.doc h2 {
  margin-top: 48px;
}

.doc ul,
.doc ol {
  padding-left: 1.4em;
}

.doc li {
  margin-bottom: 8px;
}

.code {
  /* Caddyfile blocks are tab indented, and eight columns per level pushes the
     third one off the box. */
  tab-size: 2;
  font: 400 12px/20px var(--mono);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 12px;
  white-space: pre-wrap;
  margin: 0 0 16px;
}

code {
  font: 400 0.9em/1.4 var(--mono);
}

/* The steps of a runbook. Numbers outside the text, so the sentences line up
   under each other and stay readable at the width the prose has. */
.steps {
  max-width: 46rem;
  padding-left: 1.4em;
  margin-bottom: 32px;
}

.steps > li {
  margin-bottom: 20px;
}

.steps > li > p:first-child {
  margin-top: 0;
}

/* The key and the line that carries it. Long enough to wrap, and it wraps
   rather than pushing the page sideways. */
.token {
  font: 400 12px/20px var(--mono);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 12px;
  white-space: pre-wrap;
  word-break: break-all;
  margin-bottom: 12px;
}

/* One decision, one button, and it does not need the width of the page. */
main form.one-action button {
  width: auto;
}

main p.actions {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 24px;
}
